Browse Source

ref(tsc): convert dataDownload to FC (#71092)

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

converts this file into FC and use `useApiQuery` instead of
`DeprecatedAsync`
Michelle Zhang 9 months ago
parent
commit
e84d3da2ae

+ 11 - 6
static/app/views/dataExport/dataDownload.spec.tsx

@@ -1,7 +1,7 @@
 import {OrganizationFixture} from 'sentry-fixture/organization';
 import {RouteComponentPropsFixture} from 'sentry-fixture/routeComponentPropsFixture';
 
-import {render, screen} from 'sentry-test/reactTestingLibrary';
+import {render, screen, waitForElementToBeRemoved} from 'sentry-test/reactTestingLibrary';
 import {textWithMarkupMatcher} from 'sentry-test/utils';
 
 import {ExportQueryType} from 'sentry/components/dataExport';
@@ -30,7 +30,7 @@ describe('DataDownload', function () {
     expect(getValid).toHaveBeenCalledTimes(1);
   });
 
-  it("should render the 'Error' view when appropriate", function () {
+  it("should render the 'Error' view when appropriate", async function () {
     const errors = {
       download: {
         status: 403,
@@ -43,26 +43,29 @@ describe('DataDownload', function () {
     getDataExportDetails({errors}, 403);
 
     render(<DataDownload {...RouteComponentPropsFixture()} params={mockRouteParams} />);
+    await waitForElementToBeRemoved(() => screen.queryByTestId('loading-indicator'));
     expect(screen.getByText('403 -')).toBeInTheDocument(); // Either the code or the mock is mistaken about the data return format
   });
 
-  it("should render the 'Early' view when appropriate", function () {
+  it("should render the 'Early' view when appropriate", async function () {
     const status = DownloadStatus.EARLY;
     getDataExportDetails({status});
 
     render(<DataDownload {...RouteComponentPropsFixture()} params={mockRouteParams} />);
+    await waitForElementToBeRemoved(() => screen.queryByTestId('loading-indicator'));
     expect(
       screen.getByText(textWithMarkupMatcher('What are you doing here?'))
     ).toBeInTheDocument();
     expect(screen.getByText(/were you invited/)).toBeInTheDocument();
   });
 
-  it("should render the 'Expired' view when appropriate", function () {
+  it("should render the 'Expired' view when appropriate", async function () {
     const status = DownloadStatus.EXPIRED;
     const response = {status, query: {type: ExportQueryType.ISSUES_BY_TAG}};
     getDataExportDetails(response);
 
     render(<DataDownload {...RouteComponentPropsFixture()} params={mockRouteParams} />);
+    await waitForElementToBeRemoved(() => screen.queryByTestId('loading-indicator'));
     expect(screen.getByText('This is awkward.')).toBeInTheDocument();
     expect(screen.getByRole('button', {name: 'Start a New Download'})).toHaveAttribute(
       'href',
@@ -70,11 +73,12 @@ describe('DataDownload', function () {
     );
   });
 
-  it("should render the 'Valid' view when appropriate", function () {
+  it("should render the 'Valid' view when appropriate", async function () {
     const status = DownloadStatus.VALID;
     getDataExportDetails({dateExpired, status});
 
     render(<DataDownload {...RouteComponentPropsFixture()} params={mockRouteParams} />);
+    await waitForElementToBeRemoved(() => screen.queryByTestId('loading-indicator'));
     expect(screen.getByText('All done.')).toBeInTheDocument();
     expect(screen.getByRole('button', {name: 'Download CSV'})).toHaveAttribute(
       'href',
@@ -87,7 +91,7 @@ describe('DataDownload', function () {
     ).toBeInTheDocument();
   });
 
-  it('should render the Open in Discover button when needed', function () {
+  it('should render the Open in Discover button when needed', async function () {
     const status = DownloadStatus.VALID;
     getDataExportDetails({
       dateExpired,
@@ -99,6 +103,7 @@ describe('DataDownload', function () {
     });
 
     render(<DataDownload {...RouteComponentPropsFixture()} params={mockRouteParams} />);
+    await waitForElementToBeRemoved(() => screen.queryByTestId('loading-indicator'));
     expect(screen.getByRole('button', {name: 'Open in Discover'})).toBeInTheDocument();
   });
 

+ 72 - 91
static/app/views/dataExport/dataDownload.tsx

@@ -5,13 +5,15 @@ import styled from '@emotion/styled';
 import {Button} from 'sentry/components/button';
 import {ExportQueryType} from 'sentry/components/dataExport';
 import {DateTime} from 'sentry/components/dateTime';
+import LoadingIndicator from 'sentry/components/loadingIndicator';
+import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
 import {IconDownload} from 'sentry/icons';
 import {t, tct} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
-import {browserHistory} from 'sentry/utils/browserHistory';
+import {useApiQuery} from 'sentry/utils/queryClient';
+import {useRouteContext} from 'sentry/utils/useRouteContext';
 import {normalizeUrl} from 'sentry/utils/withDomainRequired';
 import Layout from 'sentry/views/auth/layout';
-import DeprecatedAsyncView from 'sentry/views/deprecatedAsyncView';
 
 export enum DownloadStatus {
   EARLY = 'EARLY',
@@ -44,33 +46,43 @@ type Download = {
 
 type Props = {} & RouteComponentProps<RouteParams, {}>;
 
-type State = {
-  download: Download;
-  errors: {
-    download: {
-      responseJSON: {
-        detail: string;
-      };
-      status: number;
-      statusText: string;
-    };
-  };
-} & DeprecatedAsyncView['state'];
+function DataDownload({params: {orgId, dataExportId}}: Props) {
+  const {
+    data: download,
+    isLoading,
+    isError,
+    error,
+  } = useApiQuery<Download>([`/organizations/${orgId}/data-export/${dataExportId}/`], {
+    staleTime: 0,
+  });
 
-class DataDownload extends DeprecatedAsyncView<Props, State> {
-  disableErrorReport = false;
+  const route = useRouteContext();
 
-  getTitle(): string {
-    return t('Download Center');
+  if (isError) {
+    const errDetail = error?.responseJSON?.detail;
+    return (
+      <Layout>
+        <main>
+          <Header>
+            <h3>
+              {error.status} - {error.statusText}
+            </h3>
+          </Header>
+          {errDetail && (
+            <Body>
+              <p>{errDetail as string}</p>
+            </Body>
+          )}
+        </main>
+      </Layout>
+    );
   }
 
-  getEndpoints(): ReturnType<DeprecatedAsyncView['getEndpoints']> {
-    const {orgId, dataExportId} = this.props.params;
-    return [['download', `/organizations/${orgId}/data-export/${dataExportId}/`]];
+  if (isLoading) {
+    return <LoadingIndicator />;
   }
 
-  getActionLink(queryType): string {
-    const {orgId} = this.props.params;
+  const getActionLink = (queryType): string => {
     switch (queryType) {
       case ExportQueryType.ISSUES_BY_TAG:
         return `/organizations/${orgId}/issues/`;
@@ -79,9 +91,9 @@ class DataDownload extends DeprecatedAsyncView<Props, State> {
       default:
         return '/';
     }
-  }
+  };
 
-  renderDate(date: string | undefined): React.ReactNode {
+  const renderDate = (date: string | undefined): React.ReactNode => {
     if (!date) {
       return null;
     }
@@ -91,9 +103,9 @@ class DataDownload extends DeprecatedAsyncView<Props, State> {
         <DateTime date={d} />
       </strong>
     );
-  }
+  };
 
-  renderEarly(): React.ReactNode {
+  const renderEarly = (): React.ReactNode => {
     return (
       <Fragment>
         <Header>
@@ -113,11 +125,11 @@ class DataDownload extends DeprecatedAsyncView<Props, State> {
         </Body>
       </Fragment>
     );
-  }
+  };
 
-  renderExpired(): React.ReactNode {
-    const {query} = this.state.download;
-    const actionLink = this.getActionLink(query.type);
+  const renderExpired = (): React.ReactNode => {
+    const {query} = download;
+    const actionLink = getActionLink(query.type);
     return (
       <Fragment>
         <Header>
@@ -140,33 +152,29 @@ class DataDownload extends DeprecatedAsyncView<Props, State> {
         </Body>
       </Fragment>
     );
-  }
+  };
 
-  openInDiscover() {
+  const openInDiscover = () => {
+    const navigator = route.router;
     const {
-      download: {
-        query: {info},
-      },
-    } = this.state;
-    const {orgId} = this.props.params;
+      query: {info},
+    } = download;
 
     const to = {
       pathname: `/organizations/${orgId}/discover/results/`,
       query: info,
     };
 
-    browserHistory.push(normalizeUrl(to));
-  }
+    navigator.push(normalizeUrl(to));
+  };
 
-  renderOpenInDiscover() {
+  const renderOpenInDiscover = () => {
     const {
-      download: {
-        query = {
-          type: ExportQueryType.ISSUES_BY_TAG,
-          info: {},
-        },
+      query = {
+        type: ExportQueryType.ISSUES_BY_TAG,
+        info: {},
       },
-    } = this.state;
+    } = download;
 
     // default to IssuesByTag because we don't want to
     // display this unless we're sure its a discover query
@@ -175,19 +183,16 @@ class DataDownload extends DeprecatedAsyncView<Props, State> {
     return type === 'Discover' ? (
       <Fragment>
         <p>{t('Need to make changes?')}</p>
-        <Button priority="primary" onClick={() => this.openInDiscover()}>
+        <Button priority="primary" onClick={() => openInDiscover()}>
           {t('Open in Discover')}
         </Button>
         <br />
       </Fragment>
     ) : null;
-  }
+  };
 
-  renderValid(): React.ReactNode {
-    const {
-      download: {dateExpired, checksum},
-    } = this.state;
-    const {orgId, dataExportId} = this.props.params;
+  const renderValid = (): React.ReactNode => {
+    const {dateExpired, checksum} = download;
 
     return (
       <Fragment>
@@ -206,9 +211,9 @@ class DataDownload extends DeprecatedAsyncView<Props, State> {
           <p>
             {t("That link won't last forever — it expires:")}
             <br />
-            {this.renderDate(dateExpired)}
+            {renderDate(dateExpired)}
           </p>
-          {this.renderOpenInDiscover()}
+          {renderOpenInDiscover()}
           <p>
             <small>
               <strong>SHA1:{checksum}</strong>
@@ -229,50 +234,26 @@ class DataDownload extends DeprecatedAsyncView<Props, State> {
         </Body>
       </Fragment>
     );
-  }
-
-  renderError(): React.ReactNode {
-    const {
-      errors: {download: err},
-    } = this.state;
-    const errDetail = err?.responseJSON?.detail;
-    return (
-      <Layout>
-        <main>
-          <Header>
-            <h3>
-              {err.status} - {err.statusText}
-            </h3>
-          </Header>
-          {errDetail && (
-            <Body>
-              <p>{errDetail}</p>
-            </Body>
-          )}
-        </main>
-      </Layout>
-    );
-  }
+  };
 
-  renderContent(): React.ReactNode {
-    const {download} = this.state;
+  const renderContent = () => {
     switch (download.status) {
       case DownloadStatus.EARLY:
-        return this.renderEarly();
+        return renderEarly();
       case DownloadStatus.EXPIRED:
-        return this.renderExpired();
+        return renderExpired();
       default:
-        return this.renderValid();
+        return renderValid();
     }
-  }
+  };
 
-  renderBody() {
-    return (
+  return (
+    <SentryDocumentTitle title={t('Download Center')}>
       <Layout>
-        <main>{this.renderContent()}</main>
+        <main>{renderContent()}</main>
       </Layout>
-    );
-  }
+    </SentryDocumentTitle>
+  );
 }
 
 const Header = styled('header')`