From 01f83d64cc3e21ef0a1179c0aa1066bebf5b1ebb Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Mon, 17 Jun 2024 15:47:51 -0400 Subject: [PATCH 1/5] feat: my quick links --- src/components/Sidebar.test.tsx | 44 +++++ src/components/Sidebar.tsx | 36 +++- .../__snapshots__/Sidebar.test.tsx.snap | 180 ++++++++++++++++++ src/utils/links.test.ts | 16 ++ src/utils/links.ts | 8 + 5 files changed, 281 insertions(+), 3 deletions(-) diff --git a/src/components/Sidebar.test.tsx b/src/components/Sidebar.test.tsx index 8c16f7ade..93b6b1d54 100644 --- a/src/components/Sidebar.test.tsx +++ b/src/components/Sidebar.test.tsx @@ -146,6 +146,50 @@ describe('components/Sidebar.tsx', () => { ); }); + it('opens my github issues page', () => { + const openExternalLinkMock = jest.spyOn(comms, 'openExternalLink'); + + render( + + + + + , + ); + fireEvent.click(screen.getByLabelText('My Issues')); + expect(openExternalLinkMock).toHaveBeenCalledTimes(1); + expect(openExternalLinkMock).toHaveBeenCalledWith( + 'https://github.com/issues', + ); + }); + + it('opens my github pull requests page', () => { + const openExternalLinkMock = jest.spyOn(comms, 'openExternalLink'); + + render( + + + + + , + ); + fireEvent.click(screen.getByLabelText('My Pull Requests')); + expect(openExternalLinkMock).toHaveBeenCalledTimes(1); + expect(openExternalLinkMock).toHaveBeenCalledWith( + 'https://github.com/pulls', + ); + }); + it('should quit the app', () => { const quitAppMock = jest.spyOn(comms, 'quitApp'); diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index eee0c2c82..bad095124 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -1,6 +1,8 @@ import { BellIcon, GearIcon, + GitPullRequestIcon, + IssueOpenedIcon, SyncIcon, XCircleIcon, } from '@primer/octicons-react'; @@ -11,7 +13,12 @@ import { AppContext } from '../context/App'; import { BUTTON_SIDEBAR_CLASS_NAME } from '../styles/gitify'; import { cn } from '../utils/cn'; import { quitApp } from '../utils/comms'; -import { openGitHubNotifications, openGitifyRepository } from '../utils/links'; +import { + openGitHubIssues, + openGitHubNotifications, + openGitHubPulls, + openGitifyRepository, +} from '../utils/links'; import { getNotificationCount } from '../utils/notifications'; export const Sidebar: FC = () => { @@ -33,6 +40,11 @@ export const Sidebar: FC = () => { return getNotificationCount(notifications); }, [notifications]); + const hasNotifications = useMemo( + () => notificationsCount > 0, + [notificationsCount], + ); + return (
@@ -50,7 +62,7 @@ export const Sidebar: FC = () => { type="button" className={cn( 'my-1 flex cursor-pointer items-center justify-around self-stretch px-2 py-1 text-xs font-extrabold', - notificationsCount > 0 ? 'text-green-500' : 'text-white', + hasNotifications ? 'text-green-500' : 'text-white', )} onClick={() => openGitHubNotifications()} title={`${notificationsCount} Unread Notifications`} @@ -59,7 +71,25 @@ export const Sidebar: FC = () => { size={12} aria-label={`${notificationsCount} Unread Notifications`} /> - {notificationsCount > 0 && notificationsCount} + {hasNotifications && notificationsCount} + + + + +
diff --git a/src/components/__snapshots__/Sidebar.test.tsx.snap b/src/components/__snapshots__/Sidebar.test.tsx.snap index 6ef542d27..043b38dd2 100644 --- a/src/components/__snapshots__/Sidebar.test.tsx.snap +++ b/src/components/__snapshots__/Sidebar.test.tsx.snap @@ -84,6 +84,51 @@ exports[`components/Sidebar.tsx should render itself & its children (logged in) 4 + +
4 + +
4 + +
4 + +
{ ); }); + it('openGitHubIssues', () => { + openGitHubIssues(); + expect(comms.openExternalLink).toHaveBeenCalledWith( + 'https://github.com/issues', + ); + }); + + it('openGitHubPulls', () => { + openGitHubPulls(); + expect(comms.openExternalLink).toHaveBeenCalledWith( + 'https://github.com/pulls', + ); + }); + it('openAccountProfile', () => { openAccountProfile(mockGitHubCloudAccount); expect(comms.openExternalLink).toHaveBeenCalledWith( diff --git a/src/utils/links.ts b/src/utils/links.ts index 3b6f84e66..6edd44772 100644 --- a/src/utils/links.ts +++ b/src/utils/links.ts @@ -19,6 +19,14 @@ export function openGitHubNotifications() { openExternalLink('https://github.com/notifications' as Link); } +export function openGitHubIssues() { + openExternalLink('https://github.com/issues' as Link); +} + +export function openGitHubPulls() { + openExternalLink('https://github.com/pulls' as Link); +} + export function openAccountProfile(account: Account) { const url = new URL(`https://${account.hostname}`); url.pathname = account.user.login; From c07f94ea77f0291c87fd69e4dbe87de92555d5b3 Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Tue, 18 Jun 2024 11:40:50 -0400 Subject: [PATCH 2/5] refactor: extract sidebar button into component --- src/components/Sidebar.tsx | 48 ++-- src/components/buttons/SidebarButton.test.tsx | 25 ++ src/components/buttons/SidebarButton.tsx | 30 +++ .../__snapshots__/SidebarButton.test.tsx.snap | 213 ++++++++++++++++++ src/types.ts | 1 + 5 files changed, 283 insertions(+), 34 deletions(-) create mode 100644 src/components/buttons/SidebarButton.test.tsx create mode 100644 src/components/buttons/SidebarButton.tsx create mode 100644 src/components/buttons/__snapshots__/SidebarButton.test.tsx.snap diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index bad095124..3723e6f6f 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -11,7 +11,6 @@ import { useLocation, useNavigate } from 'react-router-dom'; import { Logo } from '../components/Logo'; import { AppContext } from '../context/App'; import { BUTTON_SIDEBAR_CLASS_NAME } from '../styles/gitify'; -import { cn } from '../utils/cn'; import { quitApp } from '../utils/comms'; import { openGitHubIssues, @@ -20,6 +19,7 @@ import { openGitifyRepository, } from '../utils/links'; import { getNotificationCount } from '../utils/notifications'; +import { SidebarButton } from './buttons/SidebarButton'; export const Sidebar: FC = () => { const navigate = useNavigate(); @@ -40,11 +40,6 @@ export const Sidebar: FC = () => { return getNotificationCount(notifications); }, [notifications]); - const hasNotifications = useMemo( - () => notificationsCount > 0, - [notificationsCount], - ); - return (
@@ -58,39 +53,24 @@ export const Sidebar: FC = () => { - + metric={notificationsCount} + icon={BellIcon} + onClick={() => openGitHubNotifications()} + /> - + /> - + />
diff --git a/src/components/buttons/SidebarButton.test.tsx b/src/components/buttons/SidebarButton.test.tsx new file mode 100644 index 000000000..a5efea9b7 --- /dev/null +++ b/src/components/buttons/SidebarButton.test.tsx @@ -0,0 +1,25 @@ +import { MarkGithubIcon } from '@primer/octicons-react'; +import { render } from '@testing-library/react'; +import { type ISidebarButton, SidebarButton } from './SidebarButton'; + +describe('components/buttons/SidebarButton.tsx', () => { + it('should render with metric', () => { + const props: ISidebarButton = { + title: 'Mock Sidebar Button', + metric: 1, + icon: MarkGithubIcon, + }; + const tree = render(); + expect(tree).toMatchSnapshot(); + }); + + it('should render without metric', () => { + const props: ISidebarButton = { + title: 'Mock Sidebar Button', + metric: 0, + icon: MarkGithubIcon, + }; + const tree = render(); + expect(tree).toMatchSnapshot(); + }); +}); diff --git a/src/components/buttons/SidebarButton.tsx b/src/components/buttons/SidebarButton.tsx new file mode 100644 index 000000000..18b5aafcf --- /dev/null +++ b/src/components/buttons/SidebarButton.tsx @@ -0,0 +1,30 @@ +import type { Icon } from '@primer/octicons-react'; +import type { FC } from 'react'; +import { IconColor } from '../../types'; +import { cn } from '../../utils/cn'; + +export interface ISidebarButton { + title: string; + metric?: number; + icon: Icon; + onClick?: () => void; +} + +export const SidebarButton: FC = (props: ISidebarButton) => { + const hasMetric = props?.metric > 0; + + return ( + + ); +}; diff --git a/src/components/buttons/__snapshots__/SidebarButton.test.tsx.snap b/src/components/buttons/__snapshots__/SidebarButton.test.tsx.snap new file mode 100644 index 000000000..4c3c97fe0 --- /dev/null +++ b/src/components/buttons/__snapshots__/SidebarButton.test.tsx.snap @@ -0,0 +1,213 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`components/buttons/SidebarButton.tsx should render with metric 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+ +
+ , + "container":
+ +
, + "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/buttons/SidebarButton.tsx should render without metric 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+ +
+ , + "container":
+ +
, + "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], +} +`; diff --git a/src/types.ts b/src/types.ts index 7b3d1bbd1..410c5c95a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -127,6 +127,7 @@ export enum IconColor { PURPLE = 'text-purple-500', RED = 'text-red-500', YELLOW = 'text-yellow-500 dark:text-yellow-300', + WHITE = 'text-white', } export type PullRequestApprovalIcon = { From dc674f17bb7c21c836f6e0e368833f6fb7349947 Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Tue, 18 Jun 2024 12:00:38 -0400 Subject: [PATCH 3/5] refactor: extract sidebar button into component --- src/components/Sidebar.tsx | 56 ++++++++----------- .../__snapshots__/Sidebar.test.tsx.snap | 36 ++++++------ src/components/buttons/SidebarButton.tsx | 14 ++++- .../__snapshots__/SidebarButton.test.tsx.snap | 8 +-- 4 files changed, 55 insertions(+), 59 deletions(-) diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 3723e6f6f..6e64fea0d 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -10,7 +10,6 @@ import { type FC, useContext, useMemo } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import { Logo } from '../components/Logo'; import { AppContext } from '../context/App'; -import { BUTTON_SIDEBAR_CLASS_NAME } from '../styles/gitify'; import { quitApp } from '../utils/comms'; import { openGitHubIssues, @@ -36,6 +35,11 @@ export const Sidebar: FC = () => { } }; + const refreshNotifications = () => { + navigate('/', { replace: true }); + fetchNotifications(); + }; + const notificationsCount = useMemo(() => { return getNotificationCount(notifications); }, [notifications]); @@ -61,13 +65,13 @@ export const Sidebar: FC = () => { /> openGitHubIssues()} /> openGitHubPulls()} /> @@ -76,43 +80,31 @@ export const Sidebar: FC = () => {
{isLoggedIn && ( <> - - + icon={GearIcon} + size={16} + onClick={() => toggleSettings()} + /> )} {!isLoggedIn && ( - + icon={XCircleIcon} + size={16} + onClick={() => quitApp()} + /> )}
diff --git a/src/components/__snapshots__/Sidebar.test.tsx.snap b/src/components/__snapshots__/Sidebar.test.tsx.snap index 043b38dd2..a4e4df9bf 100644 --- a/src/components/__snapshots__/Sidebar.test.tsx.snap +++ b/src/components/__snapshots__/Sidebar.test.tsx.snap @@ -63,7 +63,7 @@ exports[`components/Sidebar.tsx should render itself & its children (logged in) ); diff --git a/src/components/buttons/__snapshots__/SidebarButton.test.tsx.snap b/src/components/buttons/__snapshots__/SidebarButton.test.tsx.snap index 4c3c97fe0..30add36d3 100644 --- a/src/components/buttons/__snapshots__/SidebarButton.test.tsx.snap +++ b/src/components/buttons/__snapshots__/SidebarButton.test.tsx.snap @@ -6,7 +6,7 @@ exports[`components/buttons/SidebarButton.tsx should render with metric 1`] = ` "baseElement":
diff --git a/src/components/__snapshots__/Sidebar.test.tsx.snap b/src/components/__snapshots__/Sidebar.test.tsx.snap index a4e4df9bf..4a2dbcf07 100644 --- a/src/components/__snapshots__/Sidebar.test.tsx.snap +++ b/src/components/__snapshots__/Sidebar.test.tsx.snap @@ -84,51 +84,6 @@ exports[`components/Sidebar.tsx should render itself & its children (logged in) 4 - -
4 - -
4 - -
4 - -
{ ); }); - it('openGitHubIssues', () => { - openGitHubIssues(); - expect(comms.openExternalLink).toHaveBeenCalledWith( - 'https://github.com/issues', - ); - }); - - it('openGitHubPulls', () => { - openGitHubPulls(); - expect(comms.openExternalLink).toHaveBeenCalledWith( - 'https://github.com/pulls', - ); - }); - it('openAccountProfile', () => { openAccountProfile(mockGitHubCloudAccount); expect(comms.openExternalLink).toHaveBeenCalledWith( diff --git a/src/utils/links.ts b/src/utils/links.ts index 6edd44772..3b6f84e66 100644 --- a/src/utils/links.ts +++ b/src/utils/links.ts @@ -19,14 +19,6 @@ export function openGitHubNotifications() { openExternalLink('https://github.com/notifications' as Link); } -export function openGitHubIssues() { - openExternalLink('https://github.com/issues' as Link); -} - -export function openGitHubPulls() { - openExternalLink('https://github.com/pulls' as Link); -} - export function openAccountProfile(account: Account) { const url = new URL(`https://${account.hostname}`); url.pathname = account.user.login; From d84101e94ca0345c2d08271b3e49ecfce85bea1d Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Tue, 18 Jun 2024 12:10:15 -0400 Subject: [PATCH 5/5] refactor: extract sidebar button into component --- src/styles/gitify.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/styles/gitify.ts b/src/styles/gitify.ts index ac91a80b3..d6a2e6d47 100644 --- a/src/styles/gitify.ts +++ b/src/styles/gitify.ts @@ -1,5 +1,2 @@ export const BUTTON_CLASS_NAME = 'hover:text-gray-500 py-1 px-2 my-1 mx-2 focus:outline-none'; - -export const BUTTON_SIDEBAR_CLASS_NAME = - 'flex justify-evenly items-center bg-transparent border-0 w-full text-sm text-white my-1 py-2 cursor-pointer hover:text-gray-500 focus:outline-none disabled:text-gray-500 disabled:cursor-default';