Repository: gitify-app/gitify
Pull request #1302 (2024-06-21 02:21:30)

refactor: notification row header component


Patch:
diff --git a/src/components/NotificationRow.tsx b/src/components/NotificationRow.tsx
index e3c715b2..00424ad4 100644
--- a/src/components/NotificationRow.tsx
+++ b/src/components/NotificationRow.tsx
@@ -4,7 +4,6 @@ import {
   CommentIcon,
   FeedPersonIcon,
   IssueClosedIcon,
-  MarkGithubIcon,
   MilestoneIcon,
   ReadIcon,
   TagIcon,
@@ -29,16 +28,13 @@ import {
   getNotificationTypeIconColor,
   getPullRequestReviewIcon,
 } from '../utils/icons';
-import {
-  openNotification,
-  openRepository,
-  openUserProfile,
-} from '../utils/links';
+import { openNotification, openUserProfile } from '../utils/links';
 import { formatReason } from '../utils/reason';
 import { HoverGroup } from './HoverGroup';
 import { InteractionButton } from './buttons/InteractionButton';
 import { PillButton } from './buttons/PillButton';
 import { AvatarIcon } from './icons/AvatarIcon';
+import { NotificationHeader } from './notification/NotificationHeader';
 
 interface INotificationRow {
   notification: Notification;
@@ -108,9 +104,6 @@ export const NotificationRow: FC<INotificationRow> = ({
     notification.subject.linkedIssues?.length > 1 ? 'issues' : 'issue'
   } ${notification.subject?.linkedIssues?.join(', ')}`;
 
-  const repoAvatarUrl = notification.repository.owner.avatar_url;
-  const repoSlug = notification.repository.full_name;
-
   const groupByDate = settings.groupBy === 'DATE';
 
   return (
@@ -137,35 +130,7 @@ export const NotificationRow: FC<INotificationRow> = ({
         className="flex-1 overflow-hidden overflow-ellipsis whitespace-nowrap cursor-pointer"
         onClick={() => handleNotification()}
       >
-        {groupByDate && (
-          <div
-            className={cn(
-              'mb-1 flex items-center gap-1 text-xs font-medium',
-              Opacity.MEDIUM,
-            )}
-            title={repoSlug}
-          >
-            <span>
-              <AvatarIcon
-                title={repoSlug}
-                url={repoAvatarUrl}
-                size={Size.SMALL}
-                defaultIcon={MarkGithubIcon}
-              />
-            </span>
-            <span
-              title={repoSlug}
-              className="cursor-pointer truncate opacity-90"
-              onClick={(event: MouseEvent<HTMLElement>) => {
-                // Don't trigger onClick of parent element.
-                event.stopPropagation();
-                openRepository(notification.repository);
-              }}
-            >
-              {repoSlug}
-            </span>
-          </div>
-        )}
+        <NotificationHeader notification={notification} />
 
         <div
           className="mb-1 truncate text-sm"
diff --git a/src/components/notification/NotificationHeader.tsx b/src/components/notification/NotificationHeader.tsx
new file mode 100644
index 00000000..248db2d0
--- /dev/null
+++ b/src/components/notification/NotificationHeader.tsx
@@ -0,0 +1,54 @@
+import { MarkGithubIcon } from '@primer/octicons-react';
+import { type FC, type MouseEvent, useContext } from 'react';
+import { AppContext } from '../../context/App';
+import { Opacity, Size } from '../../types';
+import type { Notification } from '../../typesGitHub';
+import { cn } from '../../utils/cn';
+import { openRepository } from '../../utils/links';
+import { AvatarIcon } from '../icons/AvatarIcon';
+
+interface INotificationHeader {
+  notification: Notification;
+}
+
+export const NotificationHeader: FC<INotificationHeader> = ({
+  notification,
+}: INotificationHeader) => {
+  const { settings } = useContext(AppContext);
+
+  const repoAvatarUrl = notification.repository.owner.avatar_url;
+  const repoSlug = notification.repository.full_name;
+
+  const groupByDate = settings.groupBy === 'DATE';
+
+  return (
+    groupByDate && (
+      <div
+        className={cn(
+          'mb-1 flex items-center gap-1 text-xs font-medium',
+          Opacity.MEDIUM,
+        )}
+      >
+        <span>
+          <AvatarIcon
+            title={repoSlug}
+            url={repoAvatarUrl}
+            size={Size.SMALL}
+            defaultIcon={MarkGithubIcon}
+          />
+        </span>
+        <span
+          title={repoSlug}
+          className="cursor-pointer truncate opacity-90"
+          onClick={(event: MouseEvent<HTMLElement>) => {
+            // Don't trigger onClick of parent element.
+            event.stopPropagation();
+            openRepository(notification.repository);
+          }}
+        >
+          {repoSlug}
+        </span>
+      </div>
+    )
+  );
+};


Test patch:
diff --git a/src/components/__snapshots__/AccountNotifications.test.tsx.snap b/src/components/__snapshots__/AccountNotifications.test.tsx.snap
index 3c1a0f00..e63164fd 100644
--- a/src/components/__snapshots__/AccountNotifications.test.tsx.snap
+++ b/src/components/__snapshots__/AccountNotifications.test.tsx.snap
@@ -96,7 +96,6 @@ exports[`components/AccountNotifications.tsx should render itself (github.com wi
         >
           <div
             class="mb-1 flex items-center gap-1 text-xs font-medium opacity-80"
-            title="gitify-app/notifications-test"
           >
             <span>
               <img
@@ -293,7 +292,6 @@ exports[`components/AccountNotifications.tsx should render itself (github.com wi
         >
           <div
             class="mb-1 flex items-center gap-1 text-xs font-medium opacity-80"
-            title="gitify-app/notifications-test"
           >
             <span>
               <img
@@ -512,7 +510,6 @@ exports[`components/AccountNotifications.tsx should render itself (github.com wi
       >
         <div
           class="mb-1 flex items-center gap-1 text-xs font-medium opacity-80"
-          title="gitify-app/notifications-test"
         >
           <span>
             <img
@@ -709,7 +706,6 @@ exports[`components/AccountNotifications.tsx should render itself (github.com wi
       >
         <div
           class="mb-1 flex items-center gap-1 text-xs font-medium opacity-80"
-          title="gitify-app/notifications-test"
         >
           <span>
             <img
@@ -1130,11 +1126,11 @@ exports[`components/AccountNotifications.tsx should render itself (github.com wi
               height="14"
               role="img"
               style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"
-              viewBox="0 0 16 16"
+              viewBox="0 0 12 12"
               width="14"
             >
               <path
-                d="M9.78 12.78a.75.75 0 0 1-1.06 0L4.47 8.53a.75.75 0 0 1 0-1.06l4.25-4.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L6.06 8l3.72 3.72a.75.75 0 0 1 0 1.06Z"
+                d="M3.587 6.025c0 .2.1.4.2.5l3.3 3.3c.3.3.8.3 1.1 0 .3-.3.3-.8 0-1.1l-2.7-2.7 2.7-2.7c.3-.3.3-.8 0-1.1-.3-.3-.8-.3-1.1 0l-3.2 3.2c-.2.2-.3.4-.3.6Z"
               />
             </svg>
           </button>
@@ -1192,11 +1188,11 @@ exports[`components/AccountNotifications.tsx should render itself (github.com wi
             height="14"
             role="img"
             style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"
-            viewBox="0 0 16 16"
+            viewBox="0 0 12 12"
             width="14"
           >
             <path
-              d="M9.78 12.78a.75.75 0 0 1-1.06 0L4.47 8.53a.75.75 0 0 1 0-1.06l4.25-4.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L6.06 8l3.72 3.72a.75.75 0 0 1 0 1.06Z"
+              d="M3.587 6.025c0 .2.1.4.2.5l3.3 3.3c.3.3.8.3 1.1 0 .3-.3.3-.8 0-1.1l-2.7-2.7 2.7-2.7c.3-.3.3-.8 0-1.1-.3-.3-.8-.3-1.1 0l-3.2 3.2c-.2.2-.3.4-.3.6Z"
             />
           </svg>
         </button>
diff --git a/src/components/__snapshots__/NotificationRow.test.tsx.snap b/src/components/__snapshots__/NotificationRow.test.tsx.snap
index bdef7919..ba12864d 100644
--- a/src/components/__snapshots__/NotificationRow.test.tsx.snap
+++ b/src/components/__snapshots__/NotificationRow.test.tsx.snap
@@ -4668,7 +4668,6 @@ exports[`components/NotificationRow.tsx should render itself & its children - gr
         >
           <div
             class="mb-1 flex items-center gap-1 text-xs font-medium opacity-80"
-            title="gitify-app/notifications-test"
           >
             <span>
               <img
@@ -4868,7 +4867,6 @@ exports[`components/NotificationRow.tsx should render itself & its children - gr
       >
         <div
           class="mb-1 flex items-center gap-1 text-xs font-medium opacity-80"
-          title="gitify-app/notifications-test"
         >
           <span>
             <img
diff --git a/src/components/notification/NotificationHeader.test.tsx b/src/components/notification/NotificationHeader.test.tsx
new file mode 100644
index 00000000..5216da20
--- /dev/null
+++ b/src/components/notification/NotificationHeader.test.tsx
@@ -0,0 +1,61 @@
+import { fireEvent, render, screen } from '@testing-library/react';
+import { mockSettings } from '../../__mocks__/state-mocks';
+import { AppContext } from '../../context/App';
+import { GroupBy } from '../../types';
+import { mockSingleNotification } from '../../utils/api/__mocks__/response-mocks';
+import * as comms from '../../utils/comms';
+import { NotificationHeader } from './NotificationHeader';
+
+describe('components/notification/NotificationHeader.tsx', () => {
+  it('should render itself & its children - group by repositories', async () => {
+    const props = {
+      notification: mockSingleNotification,
+    };
+
+    const tree = render(
+      <AppContext.Provider
+        value={{ settings: { ...mockSettings, groupBy: GroupBy.REPOSITORY } }}
+      >
+        <NotificationHeader {...props} />
+      </AppContext.Provider>,
+    );
+    expect(tree).toMatchSnapshot();
+  });
+
+  it('should render itself & its children - group by date', async () => {
+    const props = {
+      notification: mockSingleNotification,
+    };
+
+    const tree = render(
+      <AppContext.Provider
+        value={{ settings: { ...mockSettings, groupBy: GroupBy.DATE } }}
+      >
+        <NotificationHeader {...props} />
+      </AppContext.Provider>,
+    );
+    expect(tree).toMatchSnapshot();
+  });
+
+  it('should open notification user profile - group by date', () => {
+    const openExternalLinkMock = jest.spyOn(comms, 'openExternalLink');
+
+    const props = {
+      notification: mockSingleNotification,
+    };
+
+    render(
+      <AppContext.Provider
+        value={{ settings: { ...mockSettings, groupBy: GroupBy.DATE } }}
+      >
+        <NotificationHeader {...props} />
+      </AppContext.Provider>,
+    );
+
+    fireEvent.click(screen.getByTitle(props.notification.repository.full_name));
+    expect(openExternalLinkMock).toHaveBeenCalledTimes(1);
+    expect(openExternalLinkMock).toHaveBeenCalledWith(
+      props.notification.repository.html_url,
+    );
+  });
+});
diff --git a/src/components/notification/__snapshots__/NotificationHeader.test.tsx.snap b/src/components/notification/__snapshots__/NotificationHeader.test.tsx.snap
new file mode 100644
index 00000000..4a959d29
--- /dev/null
+++ b/src/components/notification/__snapshots__/NotificationHeader.test.tsx.snap
@@ -0,0 +1,159 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`components/notification/NotificationHeader.tsx should render itself & its children - group by date 1`] = `
+{
+  "asFragment": [Function],
+  "baseElement": <body>
+    <div>
+      <div
+        class="mb-1 flex items-center gap-1 text-xs font-medium opacity-80"
+      >
+        <span>
+          <img
+            alt="gitify-app/notifications-test's avatar"
+            class="rounded-full object-cover size-5"
+            src="https://avatars.githubusercontent.com/u/133795385?s=200&v=4"
+          />
+        </span>
+        <span
+          class="cursor-pointer truncate opacity-90"
+          title="gitify-app/notifications-test"
+        >
+          gitify-app/notifications-test
+        </span>
+      </div>
+    </div>
+  </body>,
+  "container": <div>
+    <div
+      class="mb-1 flex items-center gap-1 text-xs font-medium opacity-80"
+    >
+      <span>
+        <img
+          alt="gitify-app/notifications-test's avatar"
+          class="rounded-full object-cover size-5"
+          src="https://avatars.githubusercontent.com/u/133795385?s=200&v=4"
+        />
+      </span>
+      <span
+        class="cursor-pointer truncate opacity-90"
+        title="gitify-app/notifications-test"
+      >
+        gitify-app/notifications-test
+      </span>
+    </div>
+  </div>,
+  "debug": [Function],
+  "findAllByAltText": [Function],
+  "findAllByDisplayValue": [Function],
+  "findAllByLabelText": [Function],
+  "findAllByPlaceholderText": [Function],
+  "findAllByRole": [Function],
+  "findAllByTestId": [Function],
+  "findAllByText": [Function],
+  "findAllByTitle": [Function],
+  "findByAltText": [Function],
+  "findByDisplayValue": [Function],
+  "findByLabelText": [Function],
+  "findByPlaceholderText": [Function],
+  "findByRole": [Function],
+  "findByTestId": [Function],
+  "findByText": [Function],
+  "findByTitle": [Function],
+  "getAllByAltText": [Function],
+  "getAllByDisplayValue": [Function],
+  "getAllByLabelText": [Function],
+  "getAllByPlaceholderText": [Function],
+  "getAllByRole": [Function],
+  "getAllByTestId": [Function],
+  "getAllByText": [Function],
+  "getAllByTitle": [Function],
+  "getByAltText": [Function],
+  "getByDisplayValue": [Function],
+  "getByLabelText": [Function],
+  "getByPlaceholderText": [Function],
+  "getByRole": [Function],
+  "getByTestId": [Function],
+  "getByText": [Function],
+  "getByTitle": [Function],
+  "queryAllByAltText": [Function],
+  "queryAllByDisplayValue": [Function],
+  "queryAllByLabelText": [Function],
+  "queryAllByPlaceholderText": [Function],
+  "queryAllByRole": [Function],
+  "queryAllByTestId": [Function],
+  "queryAllByText": [Function],
+  "queryAllByTitle": [Function],
+  "queryByAltText": [Function],
+  "queryByDisplayValue": [Function],
+  "queryByLabelText": [Function],
+  "queryByPlaceholderText": [Function],
+  "queryByRole": [Function],
+  "queryByTestId": [Function],
+  "queryByText": [Function],
+  "queryByTitle": [Function],
+  "rerender": [Function],
+  "unmount": [Function],
+}
+`;
+
+exports[`components/notification/NotificationHeader.tsx should render itself & its children - group by repositories 1`] = `
+{
+  "asFragment": [Function],
+  "baseElement": <body>
+    <div />
+  </body>,
+  "container": <div />,
+  "debug": [Function],
+  "findAllByAltText": [Function],
+  "findAllByDisplayValue": [Function],
+  "findAllByLabelText": [Function],
+  "findAllByPlaceholderText": [Function],
+  "findAllByRole": [Function],
+  "findAllByTestId": [Function],
+  "findAllByText": [Function],
+  "findAllByTitle": [Function],
+  "findByAltText": [Function],
+  "findByDisplayValue": [Function],
+  "findByLabelText": [Function],
+  "findByPlaceholderText": [Function],
+  "findByRole": [Function],
+  "findByTestId": [Function],
+  "findByText": [Function],
+  "findByTitle": [Function],
+  "getAllByAltText": [Function],
+  "getAllByDisplayValue": [Function],
+  "getAllByLabelText": [Function],
+  "getAllByPlaceholderText": [Function],
+  "getAllByRole": [Function],
+  "getAllByTestId": [Function],
+  "getAllByText": [Function],
+  "getAllByTitle": [Function],
+  "getByAltText": [Function],
+  "getByDisplayValue": [Function],
+  "getByLabelText": [Function],
+  "getByPlaceholderText": [Function],
+  "getByRole": [Function],
+  "getByTestId": [Function],
+  "getByText": [Function],
+  "getByTitle": [Function],
+  "queryAllByAltText": [Function],
+  "queryAllByDisplayValue": [Function],
+  "queryAllByLabelText": [Function],
+  "queryAllByPlaceholderText": [Function],
+  "queryAllByRole": [Function],
+  "queryAllByTestId": [Function],
+  "queryAllByText": [Function],
+  "queryAllByTitle": [Function],
+  "queryByAltText": [Function],
+  "queryByDisplayValue": [Function],
+  "queryByLabelText": [Function],
+  "queryByPlaceholderText": [Function],
+  "queryByRole": [Function],
+  "queryByTestId": [Function],
+  "queryByText": [Function],
+  "queryByTitle": [Function],
+  "rerender": [Function],
+  "unmount": [Function],
+}
+`;