Browse Source

ref(metric-alerts): Convert to functional component (#73145)

Priscila Oliveira 8 months ago
parent
commit
1d7bfb9558

+ 1 - 1
static/app/views/alerts/edit.tsx

@@ -10,7 +10,7 @@ import {trackAnalytics} from 'sentry/utils/analytics';
 import Teams from 'sentry/utils/teams';
 import BuilderBreadCrumbs from 'sentry/views/alerts/builder/builderBreadCrumbs';
 import IssueEditor from 'sentry/views/alerts/rules/issue';
-import MetricRulesEdit from 'sentry/views/alerts/rules/metric/edit';
+import {MetricRulesEdit} from 'sentry/views/alerts/rules/metric/edit';
 import {AlertRuleType} from 'sentry/views/alerts/types';
 
 type RouteParams = {

+ 7 - 5
static/app/views/alerts/rules/metric/edit.spec.tsx

@@ -6,7 +6,7 @@ import {initializeOrg} from 'sentry-test/initializeOrg';
 import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
 
 import {metric} from 'sentry/utils/analytics';
-import MetricRulesEdit from 'sentry/views/alerts/rules/metric/edit';
+import {MetricRulesEdit} from 'sentry/views/alerts/rules/metric/edit';
 import {AlertRuleTriggerType} from 'sentry/views/alerts/rules/metric/types';
 
 jest.mock('sentry/utils/analytics', () => ({
@@ -95,7 +95,7 @@ describe('MetricRulesEdit', function () {
     );
 
     // has existing trigger
-    expect(screen.getByTestId('critical-threshold')).toHaveValue('70');
+    expect(await screen.findByTestId('critical-threshold')).toHaveValue('70');
     expect(screen.getByTestId('resolve-threshold')).toHaveValue('36');
 
     expect(req).toHaveBeenCalled();
@@ -190,7 +190,7 @@ describe('MetricRulesEdit', function () {
     );
 
     // has existing trigger
-    expect(screen.getByTestId('critical-threshold')).toHaveValue('70');
+    expect(await screen.findByTestId('critical-threshold')).toHaveValue('70');
     expect(screen.getByTestId('warning-threshold')).toHaveValue('13');
     expect(screen.getByTestId('resolve-threshold')).toHaveValue('12');
 
@@ -226,7 +226,7 @@ describe('MetricRulesEdit', function () {
     );
   });
 
-  it('renders 404', function () {
+  it('renders 404', async function () {
     const {organization, project} = initializeOrg();
     MockApiClient.addMockResponse({
       url: `/organizations/${organization.slug}/alert-rules/1234/`,
@@ -248,6 +248,8 @@ describe('MetricRulesEdit', function () {
       />
     );
 
-    expect(screen.getByText('This alert rule could not be found.')).toBeInTheDocument();
+    expect(
+      await screen.findByText('This alert rule could not be found.')
+    ).toBeInTheDocument();
   });
 });

+ 65 - 68
static/app/views/alerts/rules/metric/edit.tsx

@@ -1,16 +1,21 @@
+import {useCallback} from 'react';
 import type {RouteComponentProps} from 'react-router';
 
 import {addErrorMessage} from 'sentry/actionCreators/indicator';
 import {Alert} from 'sentry/components/alert';
+import LoadingError from 'sentry/components/loadingError';
+import LoadingIndicator from 'sentry/components/loadingIndicator';
+import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
 import {t} from 'sentry/locale';
 import type {Organization} from 'sentry/types/organization';
 import type {Project} from 'sentry/types/project';
 import {metric} from 'sentry/utils/analytics';
+import {useApiQuery} from 'sentry/utils/queryClient';
 import routeTitleGen from 'sentry/utils/routeTitle';
+import {useNavigate} from 'sentry/utils/useNavigate';
 import {normalizeUrl} from 'sentry/utils/withDomainRequired';
 import RuleForm from 'sentry/views/alerts/rules/metric/ruleForm';
 import type {MetricRule} from 'sentry/views/alerts/rules/metric/types';
-import DeprecatedAsyncView from 'sentry/views/deprecatedAsyncView';
 
 type RouteParams = {
   projectId: string;
@@ -24,91 +29,83 @@ type Props = {
   userTeamIds: string[];
 } & RouteComponentProps<RouteParams, {}>;
 
-type State = {
-  actions: Map<string, any>;
-  rule: MetricRule; // This is temp
-} & DeprecatedAsyncView['state'];
-
-class MetricRulesEdit extends DeprecatedAsyncView<Props, State> {
-  getDefaultState() {
-    return {
-      ...super.getDefaultState(),
-      actions: new Map(),
-    };
-  }
-
-  getTitle(): string {
-    const {organization, project} = this.props;
-    const {rule} = this.state;
-    const ruleName = rule?.name;
-
-    return routeTitleGen(
-      ruleName ? t('Alert - %s', ruleName) : '',
-      organization.slug,
-      false,
-      project?.slug
-    );
-  }
-
-  getEndpoints(): ReturnType<DeprecatedAsyncView['getEndpoints']> {
-    const {organization} = this.props;
-    const {ruleId} = this.props.params;
-
-    return [['rule', `/organizations/${organization.slug}/alert-rules/${ruleId}/`]];
-  }
-
-  onRequestSuccess({stateKey, data}) {
-    if (stateKey === 'rule' && data.name) {
-      this.props.onChangeTitle(data.name);
+export function MetricRulesEdit({
+  organization,
+  params,
+  project,
+  userTeamIds,
+  onChangeTitle,
+  ...props
+}: Props) {
+  const navigate = useNavigate();
+
+  const {
+    isLoading,
+    isError,
+    data: rule,
+    error,
+  } = useApiQuery<MetricRule>(
+    [`/organizations/${organization.slug}/alert-rules/${params.ruleId}/`],
+    {
+      staleTime: 0,
+      retry: false,
+      onSuccess: data => {
+        onChangeTitle(data[0]?.name ?? '');
+      },
+      onError: ({responseText}) => {
+        const {detail} = JSON.parse(responseText ?? '');
+        if (detail) {
+          addErrorMessage(detail);
+        }
+      },
     }
-  }
-
-  onLoadAllEndpointsSuccess() {
-    const {rule} = this.state;
-    if (rule?.errors) {
-      (rule?.errors || []).map(({detail}) => addErrorMessage(detail, {append: true}));
-    }
-  }
-
-  handleSubmitSuccess = () => {
-    const {organization, router} = this.props;
-    const {ruleId} = this.props.params;
+  );
 
+  const handleSubmitSuccess = useCallback(() => {
     metric.endSpan({name: 'saveAlertRule'});
-    router.push(
+    navigate(
       normalizeUrl({
-        pathname: `/organizations/${organization.slug}/alerts/rules/details/${ruleId}/`,
+        pathname: `/organizations/${organization.slug}/alerts/rules/details/${params.ruleId}/`,
       })
     );
-  };
+  }, [params.ruleId, navigate, organization.slug]);
+
+  if (isLoading) {
+    return <LoadingIndicator />;
+  }
 
-  renderError(error?: Error, disableLog = false): React.ReactNode {
-    const {errors} = this.state;
-    const notFound = Object.values(errors).find(resp => resp && resp.status === 404);
-    if (notFound) {
+  if (isError) {
+    if (error?.status === 404) {
       return (
         <Alert type="error" showIcon>
           {t('This alert rule could not be found.')}
         </Alert>
       );
     }
-    return super.renderError(error, disableLog);
+
+    return <LoadingError />;
   }
 
-  renderBody() {
-    const {ruleId} = this.props.params;
-    const {rule} = this.state;
+  const title = routeTitleGen(
+    rule?.name ? t('Alert - %s', rule?.name) : '',
+    organization.slug,
+    false,
+    project?.slug
+  );
 
-    return (
+  return (
+    <SentryDocumentTitle title={title}>
       <RuleForm
-        {...this.props}
-        ruleId={ruleId}
+        {...props}
+        params={params}
+        project={project}
+        userTeamIds={userTeamIds}
+        organization={organization}
+        ruleId={params.ruleId}
         rule={rule}
-        onSubmitSuccess={this.handleSubmitSuccess}
+        onSubmitSuccess={handleSubmitSuccess}
         disableProjectSelector
       />
-    );
-  }
+    </SentryDocumentTitle>
+  );
 }
-
-export default MetricRulesEdit;