diff --git a/src/renderer/components/filters/AccountFilter.test.tsx b/src/renderer/components/filters/AccountFilter.test.tsx new file mode 100644 index 000000000..bf0177b5c --- /dev/null +++ b/src/renderer/components/filters/AccountFilter.test.tsx @@ -0,0 +1,25 @@ +import { renderWithProviders } from '../../__helpers__/test-utils'; +import { mockMultipleAccountNotifications } from '../../__mocks__/notifications-mocks'; +import { mockAuth } from '../../__mocks__/state-mocks'; + +import { AccountFilter } from './AccountFilter'; + +describe('renderer/components/filters/AccountFilter.tsx', () => { + it('should count notifications from the current filtered list per account', () => { + const tree = renderWithProviders(, { + accounts: mockAuth.accounts, + notifications: mockMultipleAccountNotifications, + }); + + const counters = Array.from( + tree.container.querySelectorAll('span.text-gitify-counter-text'), + ).map((el) => Number.parseInt(el.textContent ?? '0', 10)); + + expect(counters.reduce((sum, value) => sum + value, 0)).toBe( + mockMultipleAccountNotifications.reduce( + (sum, account) => sum + account.notifications.length, + 0, + ), + ); + }); +}); diff --git a/src/renderer/components/filters/FilterSection.test.tsx b/src/renderer/components/filters/FilterSection.test.tsx index c3c682b76..8a794ec90 100644 --- a/src/renderer/components/filters/FilterSection.test.tsx +++ b/src/renderer/components/filters/FilterSection.test.tsx @@ -4,7 +4,10 @@ import userEvent from '@testing-library/user-event'; import { MarkGithubIcon } from '@primer/octicons-react'; import { renderWithProviders } from '../../__helpers__/test-utils'; -import { mockMultipleAccountNotifications } from '../../__mocks__/notifications-mocks'; +import { + mockMultipleAccountNotifications, + mockPartialGitifyNotification, +} from '../../__mocks__/notifications-mocks'; import { mockSettings } from '../../__mocks__/state-mocks'; import { useFiltersStore } from '../../stores'; @@ -116,4 +119,79 @@ describe('renderer/components/filters/FilterSection.tsx', () => { expect(updateFilterSpy).toHaveBeenCalledWith(mockFilterSetting, 'closed', true); }); + + it('should count notifications from the currently filtered list', () => { + const filteredNotifications = [ + mockPartialGitifyNotification({ state: 'OPEN' }), + mockPartialGitifyNotification({ state: 'CLOSED' }), + mockPartialGitifyNotification({ state: 'MERGED' }), + ].map((notification) => ({ + account: notification.account, + notifications: [notification], + error: null, + })); + + renderWithProviders( + , + { + settings: { + ...mockSettings, + detailedNotifications: true, + }, + notifications: filteredNotifications, + }, + ); + + const counters = getCounterValues(); + expect(counters.reduce((sum, value) => sum + value, 0)).toBe(3); + }); + + it('should rescale counts when a filter in another section is toggled', () => { + renderWithProviders( + , + { + settings: { + ...mockSettings, + detailedNotifications: true, + }, + notifications: [ + mockPartialGitifyNotification({ state: 'OPEN' }), + mockPartialGitifyNotification({ state: 'CLOSED' }), + mockPartialGitifyNotification({ state: 'MERGED' }), + ].map((notification) => ({ + account: notification.account, + notifications: [notification], + error: null, + })), + }, + ); + + const before = getCounterValues(); + expect(before.reduce((sum, value) => sum + value, 0)).toBe(3); + + act(() => { + useFiltersStore.getState().updateFilter('reasons', 'mention', true); + }); + + const after = getCounterValues(); + expect(after).toEqual(before); + }); + + function getCounterValues(): number[] { + return Array.from(document.querySelectorAll('span.text-gitify-counter-text')).map((el) => + Number.parseInt(el.textContent ?? '0', 10), + ); + } }); diff --git a/src/renderer/components/filters/FilterSection.tsx b/src/renderer/components/filters/FilterSection.tsx index 9addf2d3c..f5f946071 100644 --- a/src/renderer/components/filters/FilterSection.tsx +++ b/src/renderer/components/filters/FilterSection.tsx @@ -66,12 +66,19 @@ const FilterSectionComponent = ({ {(Object.keys(filter.FILTER_TYPES) as FiltersState[K][number][]) - .sort((a, b) => - filter + .sort((a, b) => { + // Always pin the catch-all 'other' option to the end of the list. + if ((a as string) === 'other') { + return 1; + } + if ((b as string) === 'other') { + return -1; + } + return filter .getTypeDetails(a) .title.toLowerCase() - .localeCompare(filter.getTypeDetails(b).title.toLowerCase(), 'en'), - ) + .localeCompare(filter.getTypeDetails(b).title.toLowerCase(), 'en'); + }) .map((type) => { const typeDetails = filter.getTypeDetails(type); const typeTitle = typeDetails.title; diff --git a/src/renderer/components/filters/ReviewRequestTypeFilter.tsx b/src/renderer/components/filters/ReviewRequestTypeFilter.tsx index 0cae52ea3..cd30768fe 100644 --- a/src/renderer/components/filters/ReviewRequestTypeFilter.tsx +++ b/src/renderer/components/filters/ReviewRequestTypeFilter.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react'; import { GitPullRequestIcon } from '@primer/octicons-react'; -import { Text } from '@primer/react'; +import { Stack, Text } from '@primer/react'; import { reviewRequestTypeFilter } from '../../utils/notifications/filters'; import { FilterSection } from './FilterSection'; @@ -15,9 +15,16 @@ export const ReviewRequestTypeFilter: FC = () => { id="filter-review-request-types" title="Review Request Type" tooltip={ - - Filter review requests by whether you were directly requested or requested via a team. - + + + Filter review requests by whether you were directly requested or requested via a team. + Selecting an option shows only matching review requests. + + + Other shows all notifications that are not a review request, such as mentions, comments, + and assigned issues. + + } /> ); diff --git a/src/renderer/components/filters/UserTypeFilter.tsx b/src/renderer/components/filters/UserTypeFilter.tsx index 41a49b805..d6c09fcf2 100644 --- a/src/renderer/components/filters/UserTypeFilter.tsx +++ b/src/renderer/components/filters/UserTypeFilter.tsx @@ -20,7 +20,6 @@ export const UserTypeFilter: FC = () => { filterSetting="userTypes" icon={FeedPersonIcon} id="filter-user-types" - layout="horizontal" title="User Type" tooltip={ @@ -39,8 +38,15 @@ export const UserTypeFilter: FC = () => { {userTypeFilter.FILTER_TYPES.Organization.title} + + + {userTypeFilter.FILTER_TYPES.other.title} + + + Other covers notifications with no author or a non-human, non-organization author. + } /> diff --git a/src/renderer/components/filters/__snapshots__/ReasonFilter.test.tsx.snap b/src/renderer/components/filters/__snapshots__/ReasonFilter.test.tsx.snap index ab49792ac..93bd859fb 100644 --- a/src/renderer/components/filters/__snapshots__/ReasonFilter.test.tsx.snap +++ b/src/renderer/components/filters/__snapshots__/ReasonFilter.test.tsx.snap @@ -923,6 +923,62 @@ exports[`renderer/components/filters/ReasonFilter.tsx > should render itself & i 0 +
+ + + + + 0 + +
diff --git a/src/renderer/components/filters/__snapshots__/ReviewRequestTypeFilter.test.tsx.snap b/src/renderer/components/filters/__snapshots__/ReviewRequestTypeFilter.test.tsx.snap index 6add421b0..1f4341159 100644 --- a/src/renderer/components/filters/__snapshots__/ReviewRequestTypeFilter.test.tsx.snap +++ b/src/renderer/components/filters/__snapshots__/ReviewRequestTypeFilter.test.tsx.snap @@ -195,6 +195,62 @@ exports[`renderer/components/filters/ReviewRequestTypeFilter.tsx > should render 0 +
+ + + + + 4 + +
diff --git a/src/renderer/components/filters/__snapshots__/SubjectTypeFilter.test.tsx.snap b/src/renderer/components/filters/__snapshots__/SubjectTypeFilter.test.tsx.snap index 270e50710..abe9dfd74 100644 --- a/src/renderer/components/filters/__snapshots__/SubjectTypeFilter.test.tsx.snap +++ b/src/renderer/components/filters/__snapshots__/SubjectTypeFilter.test.tsx.snap @@ -460,6 +460,62 @@ exports[`renderer/components/filters/SubjectTypeFilter.tsx > should render itsel 0 +
+ + + + + 0 + +
diff --git a/src/renderer/components/filters/__snapshots__/UserTypeFilter.test.tsx.snap b/src/renderer/components/filters/__snapshots__/UserTypeFilter.test.tsx.snap index dd01ce90b..f3ef92d2c 100644 --- a/src/renderer/components/filters/__snapshots__/UserTypeFilter.test.tsx.snap +++ b/src/renderer/components/filters/__snapshots__/UserTypeFilter.test.tsx.snap @@ -77,8 +77,8 @@ exports[`renderer/components/filters/UserTypeFilter.tsx > should render itself & class="prc-Stack-Stack-UQ9k6" data-align="stretch" data-component="Stack" - data-direction="horizontal" - data-gap="normal" + data-direction="vertical" + data-gap="condensed" data-justify="start" data-padding="none" data-wrap="nowrap" @@ -197,6 +197,62 @@ exports[`renderer/components/filters/UserTypeFilter.tsx > should render itself & 1 +
+ + + + + 3 + +
diff --git a/src/renderer/routes/__screenshots__/routes.visual.test.tsx/filters-dark-classic-chromium-linux.png b/src/renderer/routes/__screenshots__/routes.visual.test.tsx/filters-dark-classic-chromium-linux.png index 7ed6b856e..4be81f34d 100644 Binary files a/src/renderer/routes/__screenshots__/routes.visual.test.tsx/filters-dark-classic-chromium-linux.png and b/src/renderer/routes/__screenshots__/routes.visual.test.tsx/filters-dark-classic-chromium-linux.png differ diff --git a/src/renderer/routes/__screenshots__/routes.visual.test.tsx/filters-light-classic-chromium-linux.png b/src/renderer/routes/__screenshots__/routes.visual.test.tsx/filters-light-classic-chromium-linux.png index d7c63aa09..e6bbae9b4 100644 Binary files a/src/renderer/routes/__screenshots__/routes.visual.test.tsx/filters-light-classic-chromium-linux.png and b/src/renderer/routes/__screenshots__/routes.visual.test.tsx/filters-light-classic-chromium-linux.png differ diff --git a/src/renderer/routes/__snapshots__/Filters.test.tsx.snap b/src/renderer/routes/__snapshots__/Filters.test.tsx.snap index b0d980eb6..f39aa0060 100644 --- a/src/renderer/routes/__snapshots__/Filters.test.tsx.snap +++ b/src/renderer/routes/__snapshots__/Filters.test.tsx.snap @@ -561,8 +561,8 @@ exports[`renderer/routes/Filters.tsx > General > should render itself & its chil class="prc-Stack-Stack-UQ9k6" data-align="stretch" data-component="Stack" - data-direction="horizontal" - data-gap="normal" + data-direction="vertical" + data-gap="condensed" data-justify="start" data-padding="none" data-wrap="nowrap" @@ -681,6 +681,62 @@ exports[`renderer/routes/Filters.tsx > General > should render itself & its chil 0 +
+ + + + + 0 + +
General > should render itself & its chil 0 +
+ + + + + 0 + +
General > should render itself & its chil 0 +
+ + + + + 0 + +
General > should render itself & its chil 0 +
+ + + + + 0 + +
@@ -2588,7 +2812,7 @@ exports[`renderer/routes/Filters.tsx > General > should render itself & its chil data-wrap="nowrap" >