Browse Source

ref(alerts): Refactor out alert badge and tooltip (#75035)

Refactor out the alert badge and tooltip between the
`activatedRuleRow.tsx` and `row.tsx`. Eventually will extend that
component to accomodate an uptime alert badge/tooltip.

Also added tests to the component

related to:
https://github.com/getsentry/team-core-product-foundations/issues/289
David Wang 7 months ago
parent
commit
414a0fe7ca

+ 3 - 17
static/app/views/alerts/list/rules/activatedRuleRow.tsx

@@ -4,7 +4,6 @@ import styled from '@emotion/styled';
 import Access from 'sentry/components/acl/access';
 import ActorAvatar from 'sentry/components/avatar/actorAvatar';
 import TeamAvatar from 'sentry/components/avatar/teamAvatar';
-import AlertBadge from 'sentry/components/badge/alertBadge';
 import {openConfirmModal} from 'sentry/components/confirm';
 import DropdownAutoComplete from 'sentry/components/dropdownAutoComplete';
 import type {ItemsBeforeFilter} from 'sentry/components/dropdownAutoComplete/types';
@@ -24,9 +23,10 @@ import {space} from 'sentry/styles/space';
 import type {Actor, Project} from 'sentry/types';
 import {useUserTeams} from 'sentry/utils/useUserTeams';
 import ActivatedMetricAlertRuleStatus from 'sentry/views/alerts/list/rules/activatedMetricAlertRuleStatus';
+import CombinedAlertBadge from 'sentry/views/alerts/list/rules/combinedAlertBadge';
 
 import type {CombinedMetricIssueAlerts, MetricAlert} from '../../types';
-import {ActivationStatus, CombinedAlertType} from '../../types';
+import {CombinedAlertType} from '../../types';
 
 type Props = {
   hasEditAccess: boolean;
@@ -53,9 +53,6 @@ function ActivatedRuleListRow({
 }: Props) {
   const {teams: userTeams} = useUserTeams();
   const [assignee, setAssignee] = useState<string>('');
-  const isWaiting =
-    !rule.activations?.length ||
-    (rule.activations?.length && rule.activations[0].isComplete);
 
   function renderLatestActivation(): React.ReactNode {
     if (!rule.activations?.length) {
@@ -197,18 +194,7 @@ function ActivatedRuleListRow({
       </AlertNameWrapper>
       <FlexCenter>
         <FlexCenter>
-          <Tooltip
-            title={tct('Metric Alert Status: [status]', {
-              status: isWaiting ? 'Ready to monitor' : 'Monitoring',
-            })}
-          >
-            <AlertBadge
-              status={rule?.latestIncident?.status}
-              activationStatus={
-                isWaiting ? ActivationStatus.WAITING : ActivationStatus.MONITORING
-              }
-            />
-          </Tooltip>
+          <CombinedAlertBadge rule={rule} />
         </FlexCenter>
         <MarginLeft>
           <ActivatedMetricAlertRuleStatus rule={rule} />

+ 73 - 0
static/app/views/alerts/list/rules/combinedAlertBadge.spec.tsx

@@ -0,0 +1,73 @@
+import {MetricRuleFixture} from 'sentry-fixture/metricRule';
+import {MetricRuleActivationFixture} from 'sentry-fixture/metricRuleActivation';
+import {ProjectAlertRuleFixture} from 'sentry-fixture/projectAlertRule';
+
+import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
+
+import {MonitorType} from 'sentry/types/alerts';
+import CombinedAlertBadge from 'sentry/views/alerts/list/rules/combinedAlertBadge';
+import {CombinedAlertType} from 'sentry/views/alerts/types';
+
+describe('CombinedAlertBadge', function () {
+  it('Renders correctly for waiting metric alert rules', async function () {
+    const rule = {
+      ...MetricRuleFixture({monitorType: MonitorType.ACTIVATED}),
+      // Cast here to inform typescript that this will always be a metric type rule
+      type: CombinedAlertType.METRIC as CombinedAlertType.METRIC,
+    };
+
+    render(<CombinedAlertBadge rule={rule} />);
+
+    await userEvent.hover(screen.getByTestId('alert-badge'));
+
+    // Renders tooltip with correct text
+    expect(
+      await screen.findByText('Metric Alert Status: Ready to monitor')
+    ).toBeInTheDocument();
+  });
+
+  it('Renders correctly for monitoring metric alert rules', async function () {
+    const rule = {
+      ...MetricRuleFixture({monitorType: MonitorType.ACTIVATED}),
+      activations: [MetricRuleActivationFixture()],
+      type: CombinedAlertType.METRIC as CombinedAlertType.METRIC,
+    };
+
+    render(<CombinedAlertBadge rule={rule} />);
+
+    await userEvent.hover(screen.getByTestId('alert-badge'));
+
+    // Renders tooltip with correct text
+    expect(
+      await screen.findByText('Metric Alert Status: Monitoring')
+    ).toBeInTheDocument();
+  });
+
+  it('Renders correctly for metric alert rules', async function () {
+    const rule = {
+      ...MetricRuleFixture(),
+      type: CombinedAlertType.METRIC as CombinedAlertType.METRIC,
+    };
+
+    render(<CombinedAlertBadge rule={rule} />);
+
+    await userEvent.hover(screen.getByTestId('alert-badge'));
+
+    // Renders tooltip with correct text
+    expect(await screen.findByText('Metric Alert Status: Resolved')).toBeInTheDocument();
+  });
+
+  it('Renders correctly for issue alert rules', async function () {
+    const rule = {
+      ...ProjectAlertRuleFixture(),
+      type: CombinedAlertType.ISSUE as CombinedAlertType.ISSUE,
+    };
+
+    render(<CombinedAlertBadge rule={rule} />);
+
+    await userEvent.hover(screen.getByTestId('alert-badge'));
+
+    // Renders tooltip with correct text
+    expect(await screen.findByText('Issue Alert')).toBeInTheDocument();
+  });
+});

+ 65 - 0
static/app/views/alerts/list/rules/combinedAlertBadge.tsx

@@ -0,0 +1,65 @@
+import AlertBadge from 'sentry/components/badge/alertBadge';
+import {Tooltip} from 'sentry/components/tooltip';
+import {t, tct} from 'sentry/locale';
+import {MonitorType} from 'sentry/types/alerts';
+import {
+  ActivationStatus,
+  type CombinedMetricIssueAlerts,
+  IncidentStatus,
+} from 'sentry/views/alerts/types';
+import {isIssueAlert} from 'sentry/views/alerts/utils';
+
+interface Props {
+  rule: CombinedMetricIssueAlerts;
+}
+
+const IncidentStatusText: Record<IncidentStatus, string> = {
+  [IncidentStatus.CRITICAL]: t('Critical'),
+  [IncidentStatus.WARNING]: t('Warning'),
+  [IncidentStatus.CLOSED]: t('Resolved'),
+  [IncidentStatus.OPENED]: t('Resolved'),
+};
+
+/**
+ * Takes in an alert rule (activated metric, metric, issue) and renders the
+ * appropriate tooltip and AlertBadge
+ */
+export default function CombinedAlertBadge({rule}: Props) {
+  const isIssueAlertInstance = isIssueAlert(rule);
+
+  if (!isIssueAlertInstance && rule.monitorType === MonitorType.ACTIVATED) {
+    const isWaiting =
+      !rule.activations?.length ||
+      (rule.activations?.length && rule.activations[0].isComplete);
+
+    return (
+      <Tooltip
+        title={tct('Metric Alert Status: [status]', {
+          status: isWaiting ? 'Ready to monitor' : 'Monitoring',
+        })}
+      >
+        <AlertBadge
+          status={rule?.latestIncident?.status}
+          activationStatus={
+            isWaiting ? ActivationStatus.WAITING : ActivationStatus.MONITORING
+          }
+        />
+      </Tooltip>
+    );
+  }
+
+  return (
+    <Tooltip
+      title={
+        isIssueAlert(rule)
+          ? t('Issue Alert')
+          : tct('Metric Alert Status: [status]', {
+              status:
+                IncidentStatusText[rule?.latestIncident?.status ?? IncidentStatus.CLOSED],
+            })
+      }
+    >
+      <AlertBadge status={rule?.latestIncident?.status} isIssue={isIssueAlert(rule)} />
+    </Tooltip>
+  );
+}

+ 3 - 26
static/app/views/alerts/list/rules/row.tsx

@@ -4,7 +4,6 @@ import styled from '@emotion/styled';
 import Access from 'sentry/components/acl/access';
 import ActorAvatar from 'sentry/components/avatar/actorAvatar';
 import TeamAvatar from 'sentry/components/avatar/teamAvatar';
-import AlertBadge from 'sentry/components/badge/alertBadge';
 import {openConfirmModal} from 'sentry/components/confirm';
 import DropdownAutoComplete from 'sentry/components/dropdownAutoComplete';
 import type {ItemsBeforeFilter} from 'sentry/components/dropdownAutoComplete/types';
@@ -24,10 +23,11 @@ import {space} from 'sentry/styles/space';
 import type {Actor, Project} from 'sentry/types';
 import {useUserTeams} from 'sentry/utils/useUserTeams';
 import AlertRuleStatus from 'sentry/views/alerts/list/rules/alertRuleStatus';
+import CombinedAlertBadge from 'sentry/views/alerts/list/rules/combinedAlertBadge';
 import {hasActiveIncident} from 'sentry/views/alerts/list/rules/utils';
 
 import type {CombinedMetricIssueAlerts} from '../../types';
-import {CombinedAlertType, IncidentStatus} from '../../types';
+import {CombinedAlertType} from '../../types';
 import {isIssueAlert} from '../../utils';
 
 type Props = {
@@ -115,13 +115,6 @@ function RuleListRow({
 
   const canEdit = ownerId ? userTeams.some(team => team.id === ownerId) : true;
 
-  const IssueStatusText: Record<IncidentStatus, string> = {
-    [IncidentStatus.CRITICAL]: t('Critical'),
-    [IncidentStatus.WARNING]: t('Warning'),
-    [IncidentStatus.CLOSED]: t('Resolved'),
-    [IncidentStatus.OPENED]: t('Resolved'),
-  };
-
   const actions: MenuItemProps[] = [
     {
       key: 'edit',
@@ -233,23 +226,7 @@ function RuleListRow({
       </AlertNameWrapper>
       <FlexCenter>
         <FlexCenter>
-          <Tooltip
-            title={
-              isIssueAlert(rule)
-                ? t('Issue Alert')
-                : tct('Metric Alert Status: [status]', {
-                    status:
-                      IssueStatusText[
-                        rule?.latestIncident?.status ?? IncidentStatus.CLOSED
-                      ],
-                  })
-            }
-          >
-            <AlertBadge
-              status={rule?.latestIncident?.status}
-              isIssue={isIssueAlert(rule)}
-            />
-          </Tooltip>
+          <CombinedAlertBadge rule={rule} />
         </FlexCenter>
         <MarginLeft>
           <AlertRuleStatus rule={rule} />