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

ref(tsc): convert apiApplications index to FC (#73091)

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

converts this file into FC and use `useApiQuery` instead of
`DeprecatedAsync`
Michelle Zhang 8 месяцев назад
Родитель
Сommit
a27dddebc8

+ 13 - 3
static/app/views/settings/account/apiApplications/index.spec.tsx

@@ -1,7 +1,13 @@
 import {ApiApplicationFixture} from 'sentry-fixture/apiApplication';
 
 import {initializeOrg} from 'sentry-test/initializeOrg';
-import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
+import {
+  render,
+  screen,
+  userEvent,
+  waitFor,
+  waitForElementToBeRemoved,
+} from 'sentry-test/reactTestingLibrary';
 
 import ApiApplications from 'sentry/views/settings/account/apiApplications';
 
@@ -12,26 +18,28 @@ describe('ApiApplications', function () {
     MockApiClient.clearMockResponses();
   });
 
-  it('renders empty', function () {
+  it('renders empty', async function () {
     MockApiClient.addMockResponse({
       url: '/api-applications/',
       body: [],
     });
 
     render(<ApiApplications {...routerProps} />);
+    await waitForElementToBeRemoved(() => screen.queryByTestId('loading-indicator'));
 
     expect(
       screen.getByText("You haven't created any applications yet.")
     ).toBeInTheDocument();
   });
 
-  it('renders', function () {
+  it('renders', async function () {
     const requestMock = MockApiClient.addMockResponse({
       url: '/api-applications/',
       body: [ApiApplicationFixture()],
     });
 
     render(<ApiApplications {...routerProps} />);
+    await waitForElementToBeRemoved(() => screen.queryByTestId('loading-indicator'));
 
     expect(requestMock).toHaveBeenCalled();
 
@@ -52,6 +60,7 @@ describe('ApiApplications', function () {
     });
 
     render(<ApiApplications {...routerProps} />);
+    await waitForElementToBeRemoved(() => screen.queryByTestId('loading-indicator'));
 
     await userEvent.click(screen.getByLabelText('Create New Application'));
 
@@ -78,6 +87,7 @@ describe('ApiApplications', function () {
     });
 
     render(<ApiApplications {...routerProps} />);
+    await waitForElementToBeRemoved(() => screen.queryByTestId('loading-indicator'));
 
     await userEvent.click(screen.getByLabelText('Remove'));
 

+ 64 - 53
static/app/views/settings/account/apiApplications/index.tsx

@@ -7,90 +7,101 @@ import {
 } from 'sentry/actionCreators/indicator';
 import {Button} from 'sentry/components/button';
 import EmptyMessage from 'sentry/components/emptyMessage';
+import LoadingError from 'sentry/components/loadingError';
+import LoadingIndicator from 'sentry/components/loadingIndicator';
 import Panel from 'sentry/components/panels/panel';
 import PanelBody from 'sentry/components/panels/panelBody';
 import PanelHeader from 'sentry/components/panels/panelHeader';
+import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
 import {IconAdd} from 'sentry/icons';
 import {t} from 'sentry/locale';
 import type {ApiApplication} from 'sentry/types';
-import DeprecatedAsyncView from 'sentry/views/deprecatedAsyncView';
+import {setApiQueryData, useApiQuery, useQueryClient} from 'sentry/utils/queryClient';
+import useApi from 'sentry/utils/useApi';
 import Row from 'sentry/views/settings/account/apiApplications/row';
 import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
 
 const ROUTE_PREFIX = '/settings/account/api/';
 
 type Props = RouteComponentProps<{}, {}>;
-type State = {
-  appList: ApiApplication[];
-} & DeprecatedAsyncView['state'];
 
-class ApiApplications extends DeprecatedAsyncView<Props, State> {
-  getEndpoints(): ReturnType<DeprecatedAsyncView['getEndpoints']> {
-    return [['appList', '/api-applications/']];
+function ApiApplications({router}: Props) {
+  const api = useApi();
+  const queryClient = useQueryClient();
+
+  const ENDPOINT = '/api-applications/';
+
+  const {
+    data: appList,
+    isLoading,
+    isError,
+    refetch,
+  } = useApiQuery<ApiApplication[]>([ENDPOINT], {
+    staleTime: 0,
+  });
+
+  if (isLoading) {
+    return <LoadingIndicator />;
   }
 
-  getTitle() {
-    return t('API Applications');
+  if (isError) {
+    return <LoadingError onRetry={refetch} />;
   }
 
-  handleCreateApplication = async () => {
+  const handleCreateApplication = async () => {
     addLoadingMessage();
 
     try {
-      const app = await this.api.requestPromise('/api-applications/', {
+      const app = await api.requestPromise(ENDPOINT, {
         method: 'POST',
       });
 
       addSuccessMessage(t('Created a new API Application'));
-      this.props.router.push(`${ROUTE_PREFIX}applications/${app.id}/`);
+      router.push(`${ROUTE_PREFIX}applications/${app.id}/`);
     } catch {
       addErrorMessage(t('Unable to remove application. Please try again.'));
     }
   };
 
-  handleRemoveApplication = (app: ApiApplication) => {
-    this.setState({
-      appList: this.state.appList.filter(a => a.id !== app.id),
-    });
+  const handleRemoveApplication = (app: ApiApplication) => {
+    setApiQueryData<any>(queryClient, [ENDPOINT], oldAppList =>
+      oldAppList.filter(a => a.id !== app.id)
+    );
   };
 
-  renderBody() {
-    const isEmpty = this.state.appList.length === 0;
-
-    return (
-      <div>
-        <SettingsPageHeader
-          title="API Applications"
-          action={
-            <Button
-              priority="primary"
-              size="sm"
-              onClick={this.handleCreateApplication}
-              icon={<IconAdd isCircled />}
-            >
-              {t('Create New Application')}
-            </Button>
-          }
-        />
-
-        <Panel>
-          <PanelHeader>{t('Application Name')}</PanelHeader>
-
-          <PanelBody>
-            {!isEmpty ? (
-              this.state.appList.map(app => (
-                <Row key={app.id} app={app} onRemove={this.handleRemoveApplication} />
-              ))
-            ) : (
-              <EmptyMessage>
-                {t("You haven't created any applications yet.")}
-              </EmptyMessage>
-            )}
-          </PanelBody>
-        </Panel>
-      </div>
-    );
-  }
+  const isEmpty = appList.length === 0;
+
+  return (
+    <SentryDocumentTitle title={t('API Applications')}>
+      <SettingsPageHeader
+        title="API Applications"
+        action={
+          <Button
+            priority="primary"
+            size="sm"
+            onClick={handleCreateApplication}
+            icon={<IconAdd isCircled />}
+          >
+            {t('Create New Application')}
+          </Button>
+        }
+      />
+
+      <Panel>
+        <PanelHeader>{t('Application Name')}</PanelHeader>
+
+        <PanelBody>
+          {!isEmpty ? (
+            appList.map(app => (
+              <Row key={app.id} app={app} onRemove={handleRemoveApplication} />
+            ))
+          ) : (
+            <EmptyMessage>{t("You haven't created any applications yet.")}</EmptyMessage>
+          )}
+        </PanelBody>
+      </Panel>
+    </SentryDocumentTitle>
+  );
 }
 
 export default ApiApplications;