Browse Source

ref(tsc): convert user feedback index to FC (#65274)

ref https://github.com/getsentry/frontend-tsc/issues/2

converts this file into FC and use `useApiQuery` instead of
`DeprecatedAsync`
Michelle Zhang 1 year ago
parent
commit
882e4948a5
2 changed files with 61 additions and 88 deletions
  1. 8 8
      static/app/views/userFeedback/index.spec.tsx
  2. 53 80
      static/app/views/userFeedback/index.tsx

+ 8 - 8
static/app/views/userFeedback/index.spec.tsx

@@ -44,7 +44,7 @@ describe('UserFeedback', function () {
     ProjectsStore.reset();
   });
 
-  it('renders', function () {
+  it('renders', async function () {
     const params = {
       organization: OrganizationFixture(),
       params: {
@@ -61,7 +61,7 @@ describe('UserFeedback', function () {
 
     render(<UserFeedback {...params} />, {context: routerContext});
 
-    expect(screen.getByText('Something bad happened')).toBeInTheDocument();
+    expect(await screen.findByText('Something bad happened')).toBeInTheDocument();
   });
 
   it('renders no project message', function () {
@@ -81,7 +81,7 @@ describe('UserFeedback', function () {
     ).toBeInTheDocument();
   });
 
-  it('renders empty state', function () {
+  it('renders empty state', async function () {
     MockApiClient.addMockResponse({
       url: '/organizations/org-slug/user-feedback/',
       body: [],
@@ -98,10 +98,10 @@ describe('UserFeedback', function () {
     };
     render(<UserFeedback {...params} />, {context: routerContext});
 
-    expect(screen.getByTestId('user-feedback-empty')).toBeInTheDocument();
+    expect(await screen.findByTestId('user-feedback-empty')).toBeInTheDocument();
   });
 
-  it('renders empty state with project query', function () {
+  it('renders empty state with project query', async function () {
     MockApiClient.addMockResponse({
       url: '/organizations/org-slug/user-feedback/',
       body: [],
@@ -124,7 +124,7 @@ describe('UserFeedback', function () {
     };
     render(<UserFeedback {...params} />, {context: routerContext});
 
-    expect(screen.getByTestId('user-feedback-empty')).toBeInTheDocument();
+    expect(await screen.findByTestId('user-feedback-empty')).toBeInTheDocument();
   });
 
   it('renders issue status filter', async function () {
@@ -155,7 +155,7 @@ describe('UserFeedback', function () {
     );
   });
 
-  it('renders empty state with multi project query', function () {
+  it('renders empty state with multi project query', async function () {
     MockApiClient.addMockResponse({
       url: '/organizations/org-slug/user-feedback/',
       body: [],
@@ -178,6 +178,6 @@ describe('UserFeedback', function () {
     };
     render(<UserFeedback {...params} />, {context: routerContext});
 
-    expect(screen.getByTestId('user-feedback-empty')).toBeInTheDocument();
+    expect(await screen.findByTestId('user-feedback-empty')).toBeInTheDocument();
   });
 });

+ 53 - 80
static/app/views/userFeedback/index.tsx

@@ -7,6 +7,7 @@ import {Button} from 'sentry/components/button';
 import {EventUserFeedback} from 'sentry/components/events/userFeedback';
 import CompactIssue from 'sentry/components/issues/compactIssue';
 import * as Layout from 'sentry/components/layouts/thirds';
+import LoadingError from 'sentry/components/loadingError';
 import LoadingIndicator from 'sentry/components/loadingIndicator';
 import NoProjectMessage from 'sentry/components/noProjectMessage';
 import {DatePageFilter} from 'sentry/components/organizations/datePageFilter';
@@ -18,50 +19,47 @@ import {PageHeadingQuestionTooltip} from 'sentry/components/pageHeadingQuestionT
 import Pagination from 'sentry/components/pagination';
 import Panel from 'sentry/components/panels/panel';
 import {SegmentedControl} from 'sentry/components/segmentedControl';
+import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
 import {Tooltip} from 'sentry/components/tooltip';
 import {t} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
-import type {Organization, UserReport} from 'sentry/types';
+import type {UserReport} from 'sentry/types';
+import {useApiQuery} from 'sentry/utils/queryClient';
+import useOrganization from 'sentry/utils/useOrganization';
 import {normalizeUrl} from 'sentry/utils/withDomainRequired';
-import withOrganization from 'sentry/utils/withOrganization';
-import type {AsyncViewState} from 'sentry/views/deprecatedAsyncView';
-import DeprecatedAsyncView from 'sentry/views/deprecatedAsyncView';
 
 import {UserFeedbackEmpty} from './userFeedbackEmpty';
 import {getQuery} from './utils';
 
-interface State extends AsyncViewState {
-  reportList: UserReport[];
-}
+interface Props extends RouteComponentProps<{}, {}> {}
 
-interface Props extends RouteComponentProps<{}, {}> {
-  organization: Organization;
-}
+function OrganizationUserFeedback({location: {search, pathname, query}, router}: Props) {
+  const organization = useOrganization();
+  const {status} = getQuery(search);
 
-class OrganizationUserFeedback extends DeprecatedAsyncView<Props, State> {
-  getEndpoints(): ReturnType<DeprecatedAsyncView['getEndpoints']> {
-    const {
-      organization,
-      location: {search},
-    } = this.props;
-
-    return [
-      [
-        'reportList',
-        `/organizations/${organization.slug}/user-feedback/`,
-        {
-          query: getQuery(search),
-        },
-      ],
-    ];
-  }
+  const unresolvedQuery = omit(query, 'status');
+  const allIssuesQuery = {...query, status: ''};
+  const hasNewFeedback = organization.features.includes('user-feedback-ui');
 
-  getTitle() {
-    return `${t('User Feedback')} - ${this.props.organization.slug}`;
-  }
+  const {
+    data: reportList,
+    isLoading,
+    isError,
+    getResponseHeader,
+  } = useApiQuery<UserReport[]>(
+    [
+      `/organizations/${organization.slug}/user-feedback/`,
+      {
+        query: getQuery(search),
+      },
+    ],
+    {staleTime: 0}
+  );
+
+  const reportListsPageLinks = getResponseHeader?.('Link');
 
-  get projectIds() {
-    const {project} = this.props.location.query;
+  function getProjectIds() {
+    const {project} = query;
 
     return Array.isArray(project)
       ? project
@@ -70,12 +68,23 @@ class OrganizationUserFeedback extends DeprecatedAsyncView<Props, State> {
         : [];
   }
 
-  renderResults() {
-    const {organization} = this.props;
-
+  function StreamBody() {
+    if (isError) {
+      return <LoadingError />;
+    }
+    if (isLoading) {
+      return (
+        <Panel>
+          <LoadingIndicator />
+        </Panel>
+      );
+    }
+    if (!reportList?.length) {
+      return <UserFeedbackEmpty projectIds={getProjectIds()} />;
+    }
     return (
       <Panel className="issue-list" data-test-id="user-feedback-list">
-        {this.state.reportList.map(item => {
+        {reportList.map(item => {
           const issue = item.issue;
           return (
             <CompactIssue key={item.id} id={issue.id} data={issue} eventId={item.eventID}>
@@ -91,44 +100,8 @@ class OrganizationUserFeedback extends DeprecatedAsyncView<Props, State> {
     );
   }
 
-  renderEmpty() {
-    return <UserFeedbackEmpty projectIds={this.projectIds} />;
-  }
-
-  renderLoading() {
-    return this.renderBody();
-  }
-
-  renderStreamBody() {
-    const {loading, reportList} = this.state;
-
-    if (loading) {
-      return (
-        <Panel>
-          <LoadingIndicator />
-        </Panel>
-      );
-    }
-
-    if (!reportList.length) {
-      return this.renderEmpty();
-    }
-
-    return this.renderResults();
-  }
-
-  renderBody() {
-    const {organization, router} = this.props;
-    const {location} = this.props;
-    const {pathname, search, query} = location;
-    const {status} = getQuery(search);
-    const {reportListPageLinks} = this.state;
-
-    const unresolvedQuery = omit(query, 'status');
-    const allIssuesQuery = {...query, status: ''};
-    const hasNewFeedback = organization.features.includes('user-feedback-ui');
-
-    return (
+  return (
+    <SentryDocumentTitle title={`${t('User Feedback')} - ${organization.slug}`}>
       <PageFiltersContainer>
         <NoProjectMessage organization={organization}>
           <Layout.Header>
@@ -158,7 +131,7 @@ class OrganizationUserFeedback extends DeprecatedAsyncView<Props, State> {
                         `/organizations/${organization.slug}/feedback/`
                       ),
                       query: {
-                        ...location.query,
+                        ...query,
                         query: undefined,
                         cursor: undefined,
                       },
@@ -194,17 +167,17 @@ class OrganizationUserFeedback extends DeprecatedAsyncView<Props, State> {
                   <SegmentedControl.Item key="">{t('All Issues')}</SegmentedControl.Item>
                 </SegmentedControl>
               </Filters>
-              {this.renderStreamBody()}
-              <Pagination pageLinks={reportListPageLinks} />
+              <StreamBody />
+              <Pagination pageLinks={reportListsPageLinks} />
             </Layout.Main>
           </Layout.Body>
         </NoProjectMessage>
       </PageFiltersContainer>
-    );
-  }
+    </SentryDocumentTitle>
+  );
 }
 
-export default withOrganization(withProfiler(OrganizationUserFeedback));
+export default withProfiler(OrganizationUserFeedback);
 
 const Filters = styled('div')`
   display: grid;