Browse Source

feat(ui) Load a user's pinned search by default if available (#12559)

If a doesn't have a query, and isn't using a saved-search we should load
their pinned search if available.

Fixes SEN-356
Mark Story 6 years ago
parent
commit
4c23e3c3a3

+ 17 - 7
src/sentry/static/sentry/app/views/organizationStream/overview.jsx

@@ -98,14 +98,8 @@ const OrganizationStream = createReactClass({
     this.fetchMemberList();
 
     // Start by getting searches first so if the user is on a saved search
-    // we load the correct data the first time.
+    // or they have a pinned search we load the correct data the first time.
     this.fetchSavedSearches();
-
-    // If we don't have a searchId there won't be more chained requests
-    // so we should fetch groups
-    if (!this.props.params.searchId) {
-      this.fetchData();
-    }
   },
 
   componentDidUpdate(prevProps, prevState) {
@@ -571,14 +565,30 @@ const OrganizationStream = createReactClass({
           savedSearchLoading: false,
         };
 
+        // Switch to the the current saved search or pinned result if available
         if (searchId) {
           const match = savedSearchList.find(search => search.id === searchId);
           newState.savedSearch = match ? match : null;
         }
+        if (
+          useOrgSavedSearches &&
+          !newState.savedSearch &&
+          this.getQuery() === DEFAULT_QUERY
+        ) {
+          const pin = savedSearchList.find(search => search.isPinned);
+          newState.savedSearch = pin ? pin : null;
+        }
         this.setState(newState);
+
+        // If we aren't loading a saved search/pin fetch data as there won't
+        // be a re-render
+        if (!newState.savedSearch) {
+          this.fetchData();
+        }
       },
       error => {
         logAjaxError(error);
+        this.fetchData();
       }
     );
   },

+ 58 - 4
tests/js/spec/views/organizationStream/overview.spec.jsx

@@ -34,6 +34,7 @@ describe('OrganizationStream', function() {
       id: '1337',
       slug: 'org-slug',
       access: ['releases'],
+      features: ['org-saved-searches'],
       projects: [project],
     });
     savedSearch = {
@@ -263,7 +264,7 @@ describe('OrganizationStream', function() {
 
     it('fetches searches and sets the savedSearch', async function() {
       const instance = wrapper.instance();
-      await instance.componentDidMount();
+      instance.componentDidMount();
       await wrapper.update();
 
       expect(instance.state.savedSearch).toBeTruthy();
@@ -271,7 +272,7 @@ describe('OrganizationStream', function() {
 
     it('uses the saved search query', async function() {
       const instance = wrapper.instance();
-      await instance.componentDidMount();
+      instance.componentDidMount();
       await wrapper.update();
 
       expect(instance.getQuery()).toEqual(savedSearch.query);
@@ -286,7 +287,7 @@ describe('OrganizationStream', function() {
 
     it('does not set the savedSearch state', async function() {
       const instance = wrapper.instance();
-      await instance.componentDidMount();
+      instance.componentDidMount();
       await wrapper.update();
 
       expect(instance.state.savedSearch).toBeNull();
@@ -327,7 +328,7 @@ describe('OrganizationStream', function() {
         wrapper.setProps({location});
 
         // Each propery change should cause a new fetch incrementing the call count.
-        expect(fetchDataMock).toHaveBeenCalledTimes(i + 1);
+        expect(fetchDataMock).toHaveBeenCalledTimes(i + 2);
       });
     });
   });
@@ -597,4 +598,57 @@ describe('OrganizationStream', function() {
       expect(wrapper.find('ErrorRobot')).toHaveLength(0);
     });
   });
+
+  describe('pinned searches', function() {
+    let pinnedSearch;
+
+    beforeEach(function() {
+      pinnedSearch = {
+        id: '888',
+        query: 'best:yes',
+        name: 'best issues',
+        isPinned: true,
+      };
+
+      MockApiClient.addMockResponse({
+        url: '/organizations/org-slug/searches/',
+        body: [savedSearch, pinnedSearch],
+      });
+
+      wrapper = shallow(<OrganizationStream {...props} />, {
+        disableLifecycleMethods: false,
+      });
+    });
+
+    it('defaults to the pinned search', async function() {
+      await wrapper.update();
+
+      const instance = wrapper.instance();
+      expect(instance.state.savedSearch).toEqual(pinnedSearch);
+    });
+
+    it('does not use pin when there is an existing query', async function() {
+      const location = {query: {query: 'timesSeen:>100'}};
+      wrapper = shallow(<OrganizationStream {...props} location={location} />, {
+        disableLifecycleMethods: false,
+      });
+      await wrapper.update();
+
+      const instance = wrapper.instance();
+      expect(instance.state.savedSearch).toEqual(null);
+    });
+
+    it('does not use pin when there is a saved search selected', async function() {
+      const params = {orgId: organization.slug, searchId: savedSearch.id};
+      wrapper = shallow(<OrganizationStream {...props} params={params} />, {
+        disableLifecycleMethods: false,
+      });
+
+      const instance = wrapper.instance();
+      instance.setState({savedSearch});
+      await wrapper.update();
+
+      expect(instance.state.savedSearch).toEqual(savedSearch);
+    });
+  });
 });