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

ref(alerts): Extract FilterBar component (#32185)

Co-authored-by: getsantry[bot] <66042841+getsantry[bot]@users.noreply.github.com>
Evan Purkhiser 3 лет назад
Родитель
Сommit
40a8946263

+ 49 - 0
static/app/views/alerts/filterBar.tsx

@@ -0,0 +1,49 @@
+import styled from '@emotion/styled';
+import {Location} from 'history';
+
+import SearchBar from 'sentry/components/searchBar';
+import {t} from 'sentry/locale';
+import space from 'sentry/styles/space';
+
+import TeamFilter from './rules/teamFilter';
+import {getQueryStatus, getTeamParams} from './utils';
+
+type Props = {
+  location: Location<any>;
+  onChangeFilter: (sectionId: string, activeFilters: Set<string>) => void;
+  onChangeSearch: (query: string) => void;
+  hasStatusFilters?: boolean;
+};
+
+function FilterBar({location, hasStatusFilters, onChangeSearch, onChangeFilter}: Props) {
+  const selectedTeams = new Set(getTeamParams(location.query.team));
+
+  const selectedStatus = hasStatusFilters
+    ? new Set(getQueryStatus(location.query.status))
+    : undefined;
+
+  return (
+    <Wrapper>
+      <TeamFilter
+        showStatus={hasStatusFilters}
+        selectedTeams={selectedTeams}
+        selectedStatus={selectedStatus}
+        handleChangeFilter={onChangeFilter}
+      />
+      <SearchBar
+        placeholder={t('Search by name')}
+        query={location.query?.name}
+        onSearch={onChangeSearch}
+      />
+    </Wrapper>
+  );
+}
+
+export default FilterBar;
+
+const Wrapper = styled('div')`
+  display: grid;
+  grid-template-columns: max-content 1fr;
+  gap: ${space(1.5)};
+  margin-bottom: ${space(1.5)};
+`;

+ 10 - 49
static/app/views/alerts/list/index.tsx

@@ -14,7 +14,6 @@ import ExternalLink from 'sentry/components/links/externalLink';
 import PageFiltersContainer from 'sentry/components/organizations/pageFilters/container';
 import Pagination from 'sentry/components/pagination';
 import {PanelTable} from 'sentry/components/panels';
-import SearchBar from 'sentry/components/searchBar';
 import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
 import {IconInfo} from 'sentry/icons';
 import {t, tct} from 'sentry/locale';
@@ -24,8 +23,9 @@ import {trackAnalyticsEvent} from 'sentry/utils/analytics';
 import Projects from 'sentry/utils/projects';
 import withOrganization from 'sentry/utils/withOrganization';
 
-import TeamFilter, {getTeamParams} from '../rules/teamFilter';
+import FilterBar from '../filterBar';
 import {Incident} from '../types';
+import {getQueryStatus, getTeamParams} from '../utils';
 
 import AlertHeader from './header';
 import Onboarding from './onboarding';
@@ -57,7 +57,7 @@ class IncidentsList extends AsyncComponent<Props, State & AsyncComponent['state'
     const {params, location} = this.props;
     const {query} = location;
 
-    const status = this.getQueryStatus(query.status);
+    const status = getQueryStatus(query.status);
     // Filtering by one status, both does nothing
     if (status.length === 1) {
       query.status = status;
@@ -69,18 +69,6 @@ class IncidentsList extends AsyncComponent<Props, State & AsyncComponent['state'
     return [['incidentList', `/organizations/${params?.orgId}/incidents/`, {query}]];
   }
 
-  getQueryStatus(status: string | string[]): string[] {
-    if (Array.isArray(status)) {
-      return status;
-    }
-
-    if (status === '') {
-      return [];
-    }
-
-    return ['open', 'closed'].includes(status) ? [status] : [];
-  }
-
   /**
    * If our incidentList is empty, determine if we've configured alert rules or
    * if the user has seen the welcome prompt.
@@ -173,28 +161,6 @@ class IncidentsList extends AsyncComponent<Props, State & AsyncComponent['state'
     });
   };
 
-  renderFilterBar() {
-    const {location} = this.props;
-    const selectedTeams = new Set(getTeamParams(location.query.team));
-    const selectedStatus = new Set(this.getQueryStatus(location.query.status));
-
-    return (
-      <FilterWrapper>
-        <TeamFilter
-          showStatus
-          selectedStatus={selectedStatus}
-          selectedTeams={selectedTeams}
-          handleChangeFilter={this.handleChangeFilter}
-        />
-        <StyledSearchBar
-          placeholder={t('Search by name')}
-          query={location.query?.name}
-          onSearch={this.handleChangeSearch}
-        />
-      </FilterWrapper>
-    );
-  }
-
   tryRenderOnboarding() {
     const {firstVisitShown} = this.state;
     const {organization} = this.props;
@@ -288,7 +254,7 @@ class IncidentsList extends AsyncComponent<Props, State & AsyncComponent['state'
   }
 
   renderBody() {
-    const {params, organization, router} = this.props;
+    const {params, organization, router, location} = this.props;
     const {orgId} = params;
 
     return (
@@ -302,7 +268,12 @@ class IncidentsList extends AsyncComponent<Props, State & AsyncComponent['state'
                   <StyledAlert icon={<IconInfo />}>
                     {t('This page only shows metric alerts.')}
                   </StyledAlert>
-                  {this.renderFilterBar()}
+                  <FilterBar
+                    location={location}
+                    onChangeFilter={this.handleChangeFilter}
+                    onChangeSearch={this.handleChangeSearch}
+                    hasStatusFilters
+                  />
                 </Fragment>
               )}
               {this.renderList()}
@@ -365,16 +336,6 @@ const StyledAlert = styled(Alert)`
   margin-bottom: ${space(1.5)};
 `;
 
-const FilterWrapper = styled('div')`
-  display: flex;
-  margin-bottom: ${space(1.5)};
-`;
-
-const StyledSearchBar = styled(SearchBar)`
-  flex-grow: 1;
-  margin-left: ${space(1.5)};
-`;
-
 const StyledLayoutBody = styled(Layout.Body)`
   margin-bottom: -20px;
 `;

+ 9 - 35
static/app/views/alerts/rules/index.tsx

@@ -11,23 +11,21 @@ import Link from 'sentry/components/links/link';
 import PageFiltersContainer from 'sentry/components/organizations/pageFilters/container';
 import Pagination from 'sentry/components/pagination';
 import {PanelTable} from 'sentry/components/panels';
-import SearchBar from 'sentry/components/searchBar';
 import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
 import {IconArrow} from 'sentry/icons';
 import {t, tct} from 'sentry/locale';
-import space from 'sentry/styles/space';
 import {Organization, PageFilters, Project} from 'sentry/types';
 import {trackAnalyticsEvent} from 'sentry/utils/analytics';
 import Projects from 'sentry/utils/projects';
 import Teams from 'sentry/utils/teams';
 import withPageFilters from 'sentry/utils/withPageFilters';
 
+import FilterBar from '../filterBar';
 import AlertHeader from '../list/header';
 import {CombinedMetricIssueAlerts} from '../types';
-import {isIssueAlert} from '../utils';
+import {getTeamParams, isIssueAlert} from '../utils';
 
 import RuleListRow from './row';
-import TeamFilter, {getTeamParams} from './teamFilter';
 
 const DOCS_URL = 'https://docs.sentry.io/product/alerts-notifications/metric-alerts/';
 
@@ -111,33 +109,15 @@ class AlertRulesList extends AsyncComponent<Props, State & AsyncComponent['state
     return this.renderBody();
   }
 
-  renderFilterBar() {
-    const {location} = this.props;
-    const selectedTeams = new Set(getTeamParams(location.query.team));
-
-    return (
-      <FilterWrapper>
-        <TeamFilter
-          selectedTeams={selectedTeams}
-          handleChangeFilter={this.handleChangeFilter}
-        />
-        <StyledSearchBar
-          placeholder={t('Search by name')}
-          query={location.query?.name}
-          onSearch={this.handleChangeSearch}
-        />
-      </FilterWrapper>
-    );
-  }
-
   renderList() {
     const {
       params: {orgId},
-      location: {query},
+      location,
       organization,
       router,
     } = this.props;
     const {loading, ruleList = [], ruleListPageLinks} = this.state;
+    const {query} = location;
 
     const allProjectsFromIncidents = new Set(
       flatten(ruleList?.map(({projects}) => projects))
@@ -160,7 +140,11 @@ class AlertRulesList extends AsyncComponent<Props, State & AsyncComponent['state
     return (
       <StyledLayoutBody>
         <Layout.Main fullWidth>
-          {this.renderFilterBar()}
+          <FilterBar
+            location={location}
+            onChangeFilter={this.handleChangeFilter}
+            onChangeSearch={this.handleChangeSearch}
+          />
           <Teams provideUserTeams>
             {({initiallyLoaded: loadedTeams, teams}) => (
               <StyledPanelTable
@@ -345,16 +329,6 @@ const StyledSortLink = styled(Link)`
   }
 `;
 
-const FilterWrapper = styled('div')`
-  display: flex;
-  margin-bottom: ${space(1.5)};
-`;
-
-const StyledSearchBar = styled(SearchBar)`
-  flex-grow: 1;
-  margin-left: ${space(1.5)};
-`;
-
 const StyledPanelTable = styled(PanelTable)`
   position: static;
   overflow: auto;

+ 36 - 0
static/app/views/alerts/utils/index.tsx

@@ -240,3 +240,39 @@ export function alertDetailsLink(organization: Organization, incident: Incident)
       : incident.alertRule.id
   }/`;
 }
+
+/**
+ * Noramlizes a status string
+ */
+export function getQueryStatus(status: string | string[]): string[] {
+  if (Array.isArray(status)) {
+    return status;
+  }
+
+  if (status === '') {
+    return [];
+  }
+
+  return ['open', 'closed'].includes(status) ? [status] : [];
+}
+
+const ALERT_LIST_QUERY_DEFAULT_TEAMS = ['myteams', 'unassigned'];
+
+/**
+ * Noramlize a team slug from the query
+ */
+export function getTeamParams(team?: string | string[]): string[] {
+  if (team === undefined) {
+    return ALERT_LIST_QUERY_DEFAULT_TEAMS;
+  }
+
+  if (team === '') {
+    return [];
+  }
+
+  if (Array.isArray(team)) {
+    return team;
+  }
+
+  return [team];
+}