Browse Source

ref(feedback): Refactor the 3 feedback integrations we have into one base hook and two views (#61411)

We don't need to copy+paste here 3x!


Also, we probably don't need any of this code in sentry, because
Feedback is only initialized in getsentry. I can followup with those
moves.
Ryan Albrecht 1 year ago
parent
commit
dca84ab03c

+ 0 - 41
static/app/components/feedback/widget/feedbackWidget.tsx

@@ -1,41 +0,0 @@
-import {useEffect} from 'react';
-import {css, Global} from '@emotion/react';
-import {Feedback, getCurrentHub} from '@sentry/react';
-
-import ConfigStore from 'sentry/stores/configStore';
-import {useLegacyStore} from 'sentry/stores/useLegacyStore';
-import theme from 'sentry/utils/theme';
-
-/**
- * Use this to display the Feedback widget in certain routes/components
- */
-export default function FeedbackWidget() {
-  const config = useLegacyStore(ConfigStore);
-  const widgetTheme = config.theme === 'dark' ? 'dark' : 'light';
-
-  useEffect(() => {
-    const hub = getCurrentHub();
-    const feedback = hub.getIntegration(Feedback);
-    const widget = feedback?.createWidget({
-      colorScheme: widgetTheme,
-      buttonLabel: 'Give Feedback',
-      submitButtonLabel: 'Send Feedback',
-      messagePlaceholder: 'What did you expect?',
-      formTitle: 'Give Feedback',
-    });
-    return () => {
-      feedback?.removeWidget(widget);
-    };
-  }, [widgetTheme]);
-
-  // z-index needs to be below our indicators which is 10001
-  return (
-    <Global
-      styles={css`
-        #sentry-feedback {
-          --z-index: ${theme.zIndex.toast - 1};
-        }
-      `}
-    />
-  );
-}

+ 22 - 0
static/app/components/feedback/widget/feedbackWidgetButton.tsx

@@ -0,0 +1,22 @@
+import {useRef} from 'react';
+
+import {Button} from 'sentry/components/button';
+import useFeedbackWidget from 'sentry/components/feedback/widget/useFeedbackWidget';
+import {IconMegaphone} from 'sentry/icons/iconMegaphone';
+import {t} from 'sentry/locale';
+
+export default function FeedbackWidgetButton() {
+  const buttonRef = useRef<HTMLButtonElement>(null);
+  const feedback = useFeedbackWidget({buttonRef});
+
+  // Do not show button if Feedback integration is not enabled
+  if (!feedback) {
+    return null;
+  }
+
+  return (
+    <Button ref={buttonRef} size="sm" icon={<IconMegaphone />}>
+      {t('Give Feedback')}
+    </Button>
+  );
+}

+ 27 - 0
static/app/components/feedback/widget/floatingFeedbackWidget.tsx

@@ -0,0 +1,27 @@
+import {css, Global} from '@emotion/react';
+
+import useFeedbackWidget from 'sentry/components/feedback/widget/useFeedbackWidget';
+import theme from 'sentry/utils/theme';
+
+/**
+ * Use this to display the Feedback widget in certain routes/components
+ */
+export default function FloatingFeedbackWidget() {
+  const feedback = useFeedbackWidget({});
+
+  // No need for global styles if Feedback integration is not enabled
+  if (!feedback) {
+    return null;
+  }
+
+  // z-index needs to be below our indicators which is 10001
+  return (
+    <Global
+      styles={css`
+        #sentry-feedback {
+          --z-index: ${theme.zIndex.toast - 1};
+        }
+      `}
+    />
+  );
+}

+ 48 - 0
static/app/components/feedback/widget/useFeedbackWidget.tsx

@@ -0,0 +1,48 @@
+import {RefObject, useEffect} from 'react';
+import {Feedback, getCurrentHub} from '@sentry/react';
+
+import {t} from 'sentry/locale';
+import ConfigStore from 'sentry/stores/configStore';
+import {useLegacyStore} from 'sentry/stores/useLegacyStore';
+
+interface Props {
+  buttonRef?: RefObject<HTMLButtonElement>;
+}
+
+export default function useFeedbackWidget({buttonRef}: Props) {
+  const config = useLegacyStore(ConfigStore);
+  const hub = getCurrentHub();
+  const feedback = hub.getIntegration(Feedback);
+
+  useEffect(() => {
+    if (!feedback) {
+      return undefined;
+    }
+
+    const options = {
+      colorScheme: config.theme === 'dark' ? ('dark' as const) : ('light' as const),
+      buttonLabel: t('Give Feedback'),
+      submitButtonLabel: t('Send Feedback'),
+      messagePlaceholder: t('What did you expect?'),
+      formTitle: t('Give Feedback'),
+    };
+
+    if (buttonRef) {
+      if (buttonRef.current) {
+        const widget = feedback.attachTo(buttonRef.current, options);
+        return () => {
+          feedback.removeWidget(widget);
+        };
+      }
+    } else {
+      const widget = feedback.createWidget(options);
+      return () => {
+        feedback.removeWidget(widget);
+      };
+    }
+
+    return undefined;
+  }, [buttonRef, config.theme, feedback]);
+
+  return feedback;
+}

+ 2 - 2
static/app/components/profiling/profileHeader.tsx

@@ -2,12 +2,12 @@ import {useCallback, useMemo} from 'react';
 import styled from '@emotion/styled';
 
 import {Button} from 'sentry/components/button';
+import FeedbackWidgetButton from 'sentry/components/feedback/widget/feedbackWidgetButton';
 import * as Layout from 'sentry/components/layouts/thirds';
 import {
   ProfilingBreadcrumbs,
   ProfilingBreadcrumbsProps,
 } from 'sentry/components/profiling/profilingBreadcrumbs';
-import ProfilingFeedbackButton from 'sentry/components/profiling/profilingFeedbackButton';
 import {t} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
 import {Event} from 'sentry/types';
@@ -87,7 +87,7 @@ function ProfileHeader({transaction, projectId, eventId}: ProfileHeaderProps) {
         </SmallerProfilingBreadcrumbsWrapper>
       </SmallerHeaderContent>
       <StyledHeaderActions>
-        <ProfilingFeedbackButton />
+        <FeedbackWidgetButton />
         {transactionTarget && (
           <Button size="sm" onClick={handleGoToTransaction} to={transactionTarget}>
             {t('Go to Transaction')}

+ 0 - 46
static/app/components/profiling/profilingFeedbackButton.tsx

@@ -1,46 +0,0 @@
-import {useEffect, useRef} from 'react';
-import {Feedback, getCurrentHub} from '@sentry/react';
-
-import {Button} from 'sentry/components/button';
-import {IconMegaphone} from 'sentry/icons/iconMegaphone';
-import {t} from 'sentry/locale';
-import ConfigStore from 'sentry/stores/configStore';
-import {useLegacyStore} from 'sentry/stores/useLegacyStore';
-
-function ProfilingFeedbackButton() {
-  const buttonRef = useRef<HTMLButtonElement | null>(null);
-  const config = useLegacyStore(ConfigStore);
-  const hub = getCurrentHub();
-  const feedback = hub.getIntegration(Feedback);
-
-  useEffect(() => {
-    if (!buttonRef.current || !feedback) {
-      return undefined;
-    }
-
-    const widget = feedback.attachTo(buttonRef.current, {
-      colorScheme: config.theme === 'dark' ? 'dark' : 'light',
-      formTitle: t('Give Feedback'),
-      submitButtonLabel: t('Send Feedback'),
-      messagePlaceholder: t('What did you expect?'),
-    });
-
-    return () => {
-      if (widget && feedback) {
-        feedback.removeWidget(widget);
-      }
-    };
-  }, [feedback, config.theme]);
-
-  if (!feedback) {
-    return null;
-  }
-
-  return (
-    <Button ref={buttonRef} data-feedback="profiling" size="sm" icon={<IconMegaphone />}>
-      {t('Give Feedback')}
-    </Button>
-  );
-}
-
-export default ProfilingFeedbackButton;

+ 2 - 2
static/app/views/ddm/layout.tsx

@@ -3,7 +3,7 @@ import styled from '@emotion/styled';
 
 import ButtonBar from 'sentry/components/buttonBar';
 import FeatureBadge from 'sentry/components/featureBadge';
-import FeedbackWidget from 'sentry/components/feedback/widget/feedbackWidget';
+import FloatingFeedbackWidget from 'sentry/components/feedback/widget/floatingFeedbackWidget';
 import {GithubFeedbackButton} from 'sentry/components/githubFeedbackButton';
 import FullViewport from 'sentry/components/layouts/fullViewport';
 import * as Layout from 'sentry/components/layouts/thirds';
@@ -49,7 +49,7 @@ function MainContent({showTraceTable}: {showTraceTable?: boolean}) {
         </Layout.HeaderActions>
       </Layout.Header>
       <Layout.Body>
-        <FeedbackWidget />
+        <FloatingFeedbackWidget />
         <Layout.Main fullWidth>
           <PaddedContainer>
             <PageFilterBar condensed>

+ 2 - 2
static/app/views/feedback/index.tsx

@@ -2,7 +2,7 @@ import {RouteComponentProps} from 'react-router';
 
 import Feature from 'sentry/components/acl/feature';
 import Alert from 'sentry/components/alert';
-import FeedbackWidget from 'sentry/components/feedback/widget/feedbackWidget';
+import FloatingFeedbackWidget from 'sentry/components/feedback/widget/floatingFeedbackWidget';
 import * as Layout from 'sentry/components/layouts/thirds';
 import NoProjectMessage from 'sentry/components/noProjectMessage';
 import {t} from 'sentry/locale';
@@ -22,7 +22,7 @@ export default function FeedbackContainer({children}: Props) {
       renderDisabled={NoAccess}
     >
       <NoProjectMessage organization={organization}>
-        <FeedbackWidget />
+        <FloatingFeedbackWidget />
         {children}
       </NoProjectMessage>
     </Feature>

+ 0 - 46
static/app/views/monitors/components/cronsFeedbackButton.tsx

@@ -1,46 +0,0 @@
-import {useEffect, useRef} from 'react';
-import {Feedback, getCurrentHub} from '@sentry/react';
-
-import {Button} from 'sentry/components/button';
-import {IconMegaphone} from 'sentry/icons/iconMegaphone';
-import ConfigStore from 'sentry/stores/configStore';
-import {useLegacyStore} from 'sentry/stores/useLegacyStore';
-
-function CronsFeedbackButton() {
-  const config = useLegacyStore(ConfigStore);
-  const ref = useRef(null);
-  const title = 'Give Feedback';
-  const widgetTheme = config.theme === 'dark' ? 'dark' : 'light';
-  const hub = getCurrentHub();
-  const feedback = hub.getIntegration(Feedback);
-
-  useEffect(() => {
-    const widget =
-      ref.current &&
-      feedback?.attachTo(ref.current, {
-        colorScheme: widgetTheme,
-        formTitle: title,
-        submitButtonLabel: 'Send Feedback',
-        messagePlaceholder: 'What did you expect?',
-      });
-
-    return () => {
-      if (widget && feedback) {
-        feedback.removeWidget(widget);
-      }
-    };
-  }, [widgetTheme, feedback]);
-
-  // Do not show button if Feedback integration is not enabled
-  if (!feedback) {
-    return null;
-  }
-
-  return (
-    <Button ref={ref} data-feedback="crons" size="sm" icon={<IconMegaphone />}>
-      {title}
-    </Button>
-  );
-}
-
-export default CronsFeedbackButton;

+ 2 - 3
static/app/views/monitors/components/monitorHeaderActions.tsx

@@ -4,6 +4,7 @@ import {deleteMonitor, updateMonitor} from 'sentry/actionCreators/monitors';
 import {Button} from 'sentry/components/button';
 import ButtonBar from 'sentry/components/buttonBar';
 import Confirm from 'sentry/components/confirm';
+import FeedbackWidgetButton from 'sentry/components/feedback/widget/feedbackWidgetButton';
 import {IconDelete, IconEdit, IconSubscribed, IconUnsubscribed} from 'sentry/icons';
 import {t} from 'sentry/locale';
 import useApi from 'sentry/utils/useApi';
@@ -12,8 +13,6 @@ import {normalizeUrl} from 'sentry/utils/withDomainRequired';
 
 import {Monitor, MonitorObjectStatus} from '../types';
 
-import CronsFeedbackButton from './cronsFeedbackButton';
-
 type Props = {
   monitor: Monitor;
   onUpdate: (data: Monitor) => void;
@@ -56,7 +55,7 @@ function MonitorHeaderActions({monitor, orgId, onUpdate}: Props) {
 
   return (
     <ButtonBar gap={1}>
-      <CronsFeedbackButton />
+      <FeedbackWidgetButton />
       <Confirm
         onConfirm={handleDelete}
         message={t('Are you sure you want to permanently delete this cron monitor?')}

Some files were not shown because too many files changed in this diff