Browse Source

ref(ui): Convert user feedback empty component to func and tests to ts and rtl (#35949)

Priscila Oliveira 2 years ago
parent
commit
800956f4c8

+ 1 - 1
static/app/views/organizationGroupDetails/groupUserFeedback.tsx

@@ -9,7 +9,7 @@ import LoadingIndicator from 'sentry/components/loadingIndicator';
 import Pagination from 'sentry/components/pagination';
 import {Group, Organization, Project, UserReport} from 'sentry/types';
 import withOrganization from 'sentry/utils/withOrganization';
-import UserFeedbackEmpty from 'sentry/views/userFeedback/userFeedbackEmpty';
+import {UserFeedbackEmpty} from 'sentry/views/userFeedback/userFeedbackEmpty';
 
 import {fetchGroupUserReports} from './utils';
 

+ 1 - 1
static/app/views/userFeedback/index.tsx

@@ -24,7 +24,7 @@ import {Organization, UserReport} from 'sentry/types';
 import withOrganization from 'sentry/utils/withOrganization';
 import AsyncView from 'sentry/views/asyncView';
 
-import UserFeedbackEmpty from './userFeedbackEmpty';
+import {UserFeedbackEmpty} from './userFeedbackEmpty';
 import {getQuery} from './utils';
 
 type State = AsyncView['state'] & {

+ 68 - 83
static/app/views/userFeedback/userFeedbackEmpty.tsx

@@ -1,4 +1,4 @@
-import {Component} from 'react';
+import {useEffect} from 'react';
 import styled from '@emotion/styled';
 import * as Sentry from '@sentry/react';
 
@@ -9,22 +9,27 @@ import ButtonBar from 'sentry/components/buttonBar';
 import EmptyStateWarning from 'sentry/components/emptyStateWarning';
 import OnboardingPanel from 'sentry/components/onboardingPanel';
 import {t} from 'sentry/locale';
-import {Organization, Project} from 'sentry/types';
 import {trackAdhocEvent, trackAnalyticsEvent} from 'sentry/utils/analytics';
-import withOrganization from 'sentry/utils/withOrganization';
-import withProjects from 'sentry/utils/withProjects';
+import useOrganization from 'sentry/utils/useOrganization';
+import useProjects from 'sentry/utils/useProjects';
 
 type Props = {
-  loadingProjects: boolean;
-  organization: Organization;
-  projects: Project[];
   projectIds?: string[];
 };
 
-class UserFeedbackEmpty extends Component<Props> {
-  componentDidMount() {
-    const {organization, projectIds} = this.props;
+export function UserFeedbackEmpty({projectIds}: Props) {
+  const {projects, initiallyLoaded} = useProjects();
+  const loadingProjects = !initiallyLoaded;
+  const organization = useOrganization();
 
+  const selectedProjects =
+    projectIds && projectIds.length
+      ? projects.filter(({id}) => projectIds.includes(id))
+      : projects;
+
+  const hasAnyFeedback = selectedProjects.some(({hasUserReports}) => hasUserReports);
+
+  useEffect(() => {
     window.sentryEmbedCallback = function (embed) {
       // Mock the embed's submit xhr to always be successful
       // NOTE: this will not have errors if the form is empty
@@ -37,7 +42,7 @@ class UserFeedbackEmpty extends Component<Props> {
       };
     };
 
-    if (this.hasAnyFeedback === false) {
+    if (hasAnyFeedback === false) {
       // send to reload only due to higher event volume
       trackAdhocEvent({
         eventKey: 'user_feedback.viewed',
@@ -45,27 +50,12 @@ class UserFeedbackEmpty extends Component<Props> {
         projects: projectIds,
       });
     }
-  }
-
-  componentWillUnmount() {
-    window.sentryEmbedCallback = null;
-  }
-
-  get selectedProjects() {
-    const {projects, projectIds} = this.props;
-
-    return projectIds && projectIds.length
-      ? projects.filter(({id}) => projectIds.includes(id))
-      : projects;
-  }
-
-  get hasAnyFeedback() {
-    return this.selectedProjects.some(({hasUserReports}) => hasUserReports);
-  }
-
-  trackAnalytics({eventKey, eventName}: {eventKey: string; eventName: string}) {
-    const {organization, projectIds} = this.props;
+    return () => {
+      window.sentryEmbedCallback = null;
+    };
+  }, [hasAnyFeedback, organization.id, projectIds]);
 
+  function trackAnalytics({eventKey, eventName}: {eventKey: string; eventName: string}) {
     trackAnalyticsEvent({
       eventKey,
       eventName,
@@ -74,63 +64,58 @@ class UserFeedbackEmpty extends Component<Props> {
     });
   }
 
-  render() {
-    // Show no user reports if waiting for projects to load or if there is no feedback
-    if (this.props.loadingProjects || this.hasAnyFeedback !== false) {
-      return (
-        <EmptyStateWarning>
-          <p>{t('Sorry, no user reports match your filters.')}</p>
-        </EmptyStateWarning>
-      );
-    }
-    // Show landing page after projects have loaded and it is confirmed no projects have feedback
+  // Show no user reports if waiting for projects to load or if there is no feedback
+  if (loadingProjects || hasAnyFeedback !== false) {
     return (
-      <OnboardingPanel image={<img src={emptyStateImg} />}>
-        <h3>{t('What do users think?')}</h3>
-        <p>
-          {t(
-            `You can't read minds. At least we hope not. Ask users for feedback on the impact of their crashes or bugs and you shall receive.`
-          )}
-        </p>
-        <ButtonList gap={1}>
-          <Button
-            external
-            priority="primary"
-            onClick={() =>
-              this.trackAnalytics({
-                eventKey: 'user_feedback.docs_clicked',
-                eventName: 'User Feedback Docs Clicked',
-              })
-            }
-            href="https://docs.sentry.io/product/user-feedback/"
-          >
-            {t('Read the docs')}
-          </Button>
-          <Button
-            onClick={() => {
-              Sentry.showReportDialog({
-                // should never make it to the Sentry API, but just in case, use throwaway id
-                eventId: '00000000000000000000000000000000',
-              });
-
-              this.trackAnalytics({
-                eventKey: 'user_feedback.dialog_opened',
-                eventName: 'User Feedback Dialog Opened',
-              });
-            }}
-          >
-            {t('See an example')}
-          </Button>
-        </ButtonList>
-      </OnboardingPanel>
+      <EmptyStateWarning>
+        <p>{t('Sorry, no user reports match your filters.')}</p>
+      </EmptyStateWarning>
     );
   }
+
+  // Show landing page after projects have loaded and it is confirmed no projects have feedback
+  return (
+    <OnboardingPanel image={<img src={emptyStateImg} />}>
+      <h3>{t('What do users think?')}</h3>
+      <p>
+        {t(
+          `You can't read minds. At least we hope not. Ask users for feedback on the impact of their crashes or bugs and you shall receive.`
+        )}
+      </p>
+      <ButtonList gap={1}>
+        <Button
+          external
+          priority="primary"
+          onClick={() =>
+            trackAnalytics({
+              eventKey: 'user_feedback.docs_clicked',
+              eventName: 'User Feedback Docs Clicked',
+            })
+          }
+          href="https://docs.sentry.io/product/user-feedback/"
+        >
+          {t('Read the docs')}
+        </Button>
+        <Button
+          onClick={() => {
+            Sentry.showReportDialog({
+              // should never make it to the Sentry API, but just in case, use throwaway id
+              eventId: '00000000000000000000000000000000',
+            });
+
+            trackAnalytics({
+              eventKey: 'user_feedback.dialog_opened',
+              eventName: 'User Feedback Dialog Opened',
+            });
+          }}
+        >
+          {t('See an example')}
+        </Button>
+      </ButtonList>
+    </OnboardingPanel>
+  );
 }
 
 const ButtonList = styled(ButtonBar)`
   grid-template-columns: repeat(auto-fit, minmax(130px, max-content));
 `;
-
-export {UserFeedbackEmpty};
-
-export default withOrganization(withProjects(UserFeedbackEmpty));

+ 0 - 89
tests/js/spec/views/userFeedback/userFeedbackEmpty.spec.jsx

@@ -1,89 +0,0 @@
-import {mountWithTheme} from 'sentry-test/enzyme';
-
-import {UserFeedbackEmpty} from 'sentry/views/userFeedback/userFeedbackEmpty';
-
-describe('UserFeedbackEmpty', function () {
-  const project = TestStubs.Project({id: '1'});
-  const projectWithReports = TestStubs.Project({id: '2', hasUserReports: true});
-  const projectWithoutReports = TestStubs.Project({id: '3'});
-  const organization = TestStubs.Organization();
-
-  it('renders empty', function () {
-    mountWithTheme(<UserFeedbackEmpty projects={[]} organization={organization} />);
-  });
-
-  it('renders landing for project with no user feedback', function () {
-    const wrapper = mountWithTheme(
-      <UserFeedbackEmpty projects={[project]} organization={organization} />
-    );
-
-    expect(wrapper.find('OnboardingPanel').exists()).toBe(true);
-  });
-
-  it('renders warning for project with any user feedback', function () {
-    const wrapper = mountWithTheme(
-      <UserFeedbackEmpty projects={[projectWithReports]} organization={organization} />
-    );
-
-    expect(wrapper.find('EmptyStateWarning').exists()).toBe(true);
-  });
-
-  it('renders warning for projects with any user feedback', function () {
-    const wrapper = mountWithTheme(
-      <UserFeedbackEmpty
-        projects={[project, projectWithReports]}
-        organization={organization}
-      />
-    );
-
-    expect(wrapper.find('EmptyStateWarning').exists()).toBe(true);
-  });
-
-  it('renders warning for project query with user feedback', function () {
-    const wrapper = mountWithTheme(
-      <UserFeedbackEmpty
-        projects={[project, projectWithReports]}
-        organization={organization}
-        projectIds={[projectWithReports.id]}
-      />
-    );
-
-    expect(wrapper.find('EmptyStateWarning').exists()).toBe(true);
-  });
-
-  it('renders landing for project query without any user feedback', function () {
-    const wrapper = mountWithTheme(
-      <UserFeedbackEmpty
-        projects={[project, projectWithReports]}
-        organization={organization}
-        projectIds={[project.id]}
-      />
-    );
-
-    expect(wrapper.find('OnboardingPanel').exists()).toBe(true);
-  });
-
-  it('renders warning for multi project query with any user feedback', function () {
-    const wrapper = mountWithTheme(
-      <UserFeedbackEmpty
-        projects={[project, projectWithReports]}
-        organization={organization}
-        projectIds={[project.id, projectWithReports.id]}
-      />
-    );
-
-    expect(wrapper.find('EmptyStateWarning').exists()).toBe(true);
-  });
-
-  it('renders landing for multi project query without any user feedback', function () {
-    const wrapper = mountWithTheme(
-      <UserFeedbackEmpty
-        projects={[project, projectWithoutReports]}
-        organization={organization}
-        projectIds={[project.id, projectWithoutReports.id]}
-      />
-    );
-
-    expect(wrapper.find('UserFeedbackEmpty').exists()).toBe(true);
-  });
-});

+ 128 - 0
tests/js/spec/views/userFeedback/userFeedbackEmpty.spec.tsx

@@ -0,0 +1,128 @@
+import {reactHooks, render, screen} from 'sentry-test/reactTestingLibrary';
+
+import ProjectsStore from 'sentry/stores/projectsStore';
+import {OrganizationContext} from 'sentry/views/organizationContext';
+import {UserFeedbackEmpty} from 'sentry/views/userFeedback/userFeedbackEmpty';
+
+describe('UserFeedbackEmpty', function () {
+  const project = TestStubs.Project({id: '1'});
+  const projectWithReports = TestStubs.Project({id: '2', hasUserReports: true});
+  const projectWithoutReports = TestStubs.Project({id: '3'});
+  const organization = TestStubs.Organization();
+
+  it('renders empty', function () {
+    render(
+      <OrganizationContext.Provider value={organization}>
+        <UserFeedbackEmpty />)
+      </OrganizationContext.Provider>
+    );
+  });
+
+  it('renders landing for project with no user feedback', function () {
+    reactHooks.act(() => void ProjectsStore.loadInitialData([project]));
+
+    render(
+      <OrganizationContext.Provider value={organization}>
+        <UserFeedbackEmpty />)
+      </OrganizationContext.Provider>
+    );
+
+    expect(
+      screen.getByRole('heading', {name: 'What do users think?'})
+    ).toBeInTheDocument();
+  });
+
+  it('renders warning for project with any user feedback', function () {
+    reactHooks.act(() => void ProjectsStore.loadInitialData([projectWithReports]));
+
+    render(
+      <OrganizationContext.Provider value={organization}>
+        <UserFeedbackEmpty />)
+      </OrganizationContext.Provider>
+    );
+
+    expect(
+      screen.getByText('Sorry, no user reports match your filters.')
+    ).toBeInTheDocument();
+  });
+
+  it('renders warning for projects with any user feedback', function () {
+    reactHooks.act(
+      () => void ProjectsStore.loadInitialData([project, projectWithReports])
+    );
+
+    render(
+      <OrganizationContext.Provider value={organization}>
+        <UserFeedbackEmpty />)
+      </OrganizationContext.Provider>
+    );
+
+    expect(
+      screen.getByText('Sorry, no user reports match your filters.')
+    ).toBeInTheDocument();
+  });
+
+  it('renders warning for project query with user feedback', function () {
+    reactHooks.act(
+      () => void ProjectsStore.loadInitialData([project, projectWithReports])
+    );
+
+    render(
+      <OrganizationContext.Provider value={organization}>
+        <UserFeedbackEmpty projectIds={[projectWithReports.id]} />)
+      </OrganizationContext.Provider>
+    );
+
+    expect(
+      screen.getByText('Sorry, no user reports match your filters.')
+    ).toBeInTheDocument();
+  });
+
+  it('renders landing for project query without any user feedback', function () {
+    reactHooks.act(
+      () => void ProjectsStore.loadInitialData([project, projectWithReports])
+    );
+
+    render(
+      <OrganizationContext.Provider value={organization}>
+        <UserFeedbackEmpty projectIds={[project.id]} />)
+      </OrganizationContext.Provider>
+    );
+
+    expect(
+      screen.getByRole('heading', {name: 'What do users think?'})
+    ).toBeInTheDocument();
+  });
+
+  it('renders warning for multi project query with any user feedback', function () {
+    reactHooks.act(
+      () => void ProjectsStore.loadInitialData([project, projectWithReports])
+    );
+
+    render(
+      <OrganizationContext.Provider value={organization}>
+        <UserFeedbackEmpty projectIds={[project.id, projectWithReports.id]} />)
+      </OrganizationContext.Provider>
+    );
+
+    expect(
+      screen.getByText('Sorry, no user reports match your filters.')
+    ).toBeInTheDocument();
+  });
+
+  it('renders landing for multi project query without any user feedback', function () {
+    reactHooks.act(
+      () => void ProjectsStore.loadInitialData([project, projectWithoutReports])
+    );
+
+    render(
+      <OrganizationContext.Provider value={organization}>
+        <UserFeedbackEmpty projectIds={[project.id, projectWithoutReports.id]} />)
+      </OrganizationContext.Provider>
+    );
+
+    expect(
+      screen.getByRole('heading', {name: 'What do users think?'})
+    ).toBeInTheDocument();
+  });
+});