Skip to content

Commit 99a3dc6

Browse files
authored
feat: add state filter (#1859)
* feat: add state filter Signed-off-by: Adam Setch <[email protected]> * update coverage Signed-off-by: Adam Setch <[email protected]> * update coverage Signed-off-by: Adam Setch <[email protected]> * merge main Signed-off-by: Adam Setch <[email protected]> --------- Signed-off-by: Adam Setch <[email protected]>
1 parent da58488 commit 99a3dc6

20 files changed

+2564
-84
lines changed

src/renderer/__mocks__/state-mocks.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ const mockFilters: FilterSettingsState = {
109109
filterUserTypes: [],
110110
filterIncludeHandles: [],
111111
filterExcludeHandles: [],
112+
filterStates: [],
112113
filterReasons: [],
113114
};
114115

src/renderer/components/filters/ReasonFilter.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { act, fireEvent, render, screen } from '@testing-library/react';
22
import { MemoryRouter } from 'react-router-dom';
3+
import { mockAccountNotifications } from '../../__mocks__/notifications-mocks';
34
import { mockSettings } from '../../__mocks__/state-mocks';
45
import { AppContext } from '../../context/App';
56
import { ReasonFilter } from './ReasonFilter';
@@ -12,7 +13,7 @@ describe('renderer/components/filters/ReasonFilter.tsx', () => {
1213
<AppContext.Provider
1314
value={{
1415
settings: mockSettings,
15-
notifications: [],
16+
notifications: mockAccountNotifications,
1617
}}
1718
>
1819
<MemoryRouter>
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { act, fireEvent, render, screen } from '@testing-library/react';
2+
import { MemoryRouter } from 'react-router-dom';
3+
import { mockAccountNotifications } from '../../__mocks__/notifications-mocks';
4+
import { mockSettings } from '../../__mocks__/state-mocks';
5+
import { AppContext } from '../../context/App';
6+
import type { SettingsState } from '../../types';
7+
import { StateFilter } from './StateFilter';
8+
9+
describe('renderer/components/filters/StateFilter.tsx', () => {
10+
const updateFilter = jest.fn();
11+
12+
describe('should render itself & its children', () => {
13+
it('with detailed notifications enabled', () => {
14+
const tree = render(
15+
<AppContext.Provider
16+
value={{
17+
settings: {
18+
...mockSettings,
19+
detailedNotifications: true,
20+
} as SettingsState,
21+
notifications: mockAccountNotifications,
22+
}}
23+
>
24+
<MemoryRouter>
25+
<StateFilter />
26+
</MemoryRouter>
27+
</AppContext.Provider>,
28+
);
29+
30+
expect(tree).toMatchSnapshot();
31+
});
32+
33+
it('with detailed notifications disabled', () => {
34+
const tree = render(
35+
<AppContext.Provider
36+
value={{
37+
settings: {
38+
...mockSettings,
39+
detailedNotifications: false,
40+
} as SettingsState,
41+
notifications: mockAccountNotifications,
42+
}}
43+
>
44+
<MemoryRouter>
45+
<StateFilter />
46+
</MemoryRouter>
47+
</AppContext.Provider>,
48+
);
49+
50+
expect(tree).toMatchSnapshot();
51+
});
52+
});
53+
54+
it('should be able to toggle user type - none already set', async () => {
55+
await act(async () => {
56+
render(
57+
<AppContext.Provider
58+
value={{
59+
settings: {
60+
...mockSettings,
61+
filterStates: [],
62+
},
63+
notifications: [],
64+
updateFilter,
65+
}}
66+
>
67+
<MemoryRouter>
68+
<StateFilter />
69+
</MemoryRouter>
70+
</AppContext.Provider>,
71+
);
72+
});
73+
74+
fireEvent.click(screen.getByLabelText('Open'));
75+
76+
expect(updateFilter).toHaveBeenCalledWith('filterStates', 'open', true);
77+
78+
expect(
79+
screen.getByLabelText('Open').parentNode.parentNode,
80+
).toMatchSnapshot();
81+
});
82+
83+
it('should be able to toggle user type - some filters already set', async () => {
84+
await act(async () => {
85+
render(
86+
<AppContext.Provider
87+
value={{
88+
settings: {
89+
...mockSettings,
90+
filterStates: ['open'],
91+
},
92+
notifications: [],
93+
updateFilter,
94+
}}
95+
>
96+
<MemoryRouter>
97+
<StateFilter />
98+
</MemoryRouter>
99+
</AppContext.Provider>,
100+
);
101+
});
102+
103+
fireEvent.click(screen.getByLabelText('Closed'));
104+
105+
expect(updateFilter).toHaveBeenCalledWith('filterStates', 'closed', true);
106+
107+
expect(
108+
screen.getByLabelText('Closed').parentNode.parentNode,
109+
).toMatchSnapshot();
110+
});
111+
});
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { type FC, useContext } from 'react';
2+
3+
import { BellIcon } from '@primer/octicons-react';
4+
import { Stack, Text } from '@primer/react';
5+
6+
import { AppContext } from '../../context/App';
7+
import type { FilterStateType } from '../../types';
8+
import {
9+
FILTERS_STATE_TYPES,
10+
getStateDetails,
11+
getStateFilterCount,
12+
isStateFilterSet,
13+
} from '../../utils/notifications/filters/state';
14+
import { Checkbox } from '../fields/Checkbox';
15+
import { Tooltip } from '../fields/Tooltip';
16+
import { Title } from '../primitives/Title';
17+
18+
export const StateFilter: FC = () => {
19+
const { updateFilter, settings, notifications } = useContext(AppContext);
20+
21+
return (
22+
<fieldset id="filter-state" className="mb-3">
23+
<Stack direction="horizontal" gap="condensed" align="baseline">
24+
<Title icon={BellIcon}>State</Title>
25+
<Tooltip
26+
name="tooltip-filter-state"
27+
tooltip={
28+
<Stack direction="vertical" gap="condensed">
29+
<Text>Filter notifications by state.</Text>
30+
<Text className="text-gitify-caution">
31+
⚠️ This filter requires the{' '}
32+
<Text as="strong">Detailed Notifications</Text> setting to be
33+
enabled.
34+
</Text>
35+
</Stack>
36+
}
37+
/>
38+
</Stack>
39+
40+
<Stack direction="vertical" gap="condensed">
41+
{Object.keys(FILTERS_STATE_TYPES).map((stateType: FilterStateType) => {
42+
const stateDetails = getStateDetails(stateType);
43+
const stateTitle = stateDetails.title;
44+
const stateDescription = stateDetails.description;
45+
const isStateChecked = isStateFilterSet(settings, stateType);
46+
const stateCount = getStateFilterCount(notifications, stateType);
47+
48+
return (
49+
<Checkbox
50+
key={stateType}
51+
name={stateTitle}
52+
label={stateTitle}
53+
checked={isStateChecked}
54+
onChange={(evt) =>
55+
updateFilter('filterStates', stateType, evt.target.checked)
56+
}
57+
tooltip={
58+
stateDescription ? <Text>{stateDescription}</Text> : null
59+
}
60+
disabled={!settings.detailedNotifications}
61+
counter={stateCount}
62+
/>
63+
);
64+
})}
65+
</Stack>
66+
</fieldset>
67+
);
68+
};

src/renderer/components/filters/UserHandleFilter.test.tsx

Lines changed: 40 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { act, fireEvent, render, screen } from '@testing-library/react';
22
import { MemoryRouter } from 'react-router-dom';
3+
import { mockAccountNotifications } from '../../__mocks__/notifications-mocks';
34
import { mockSettings } from '../../__mocks__/state-mocks';
45
import { AppContext } from '../../context/App';
56
import type { SettingsState } from '../../types';
@@ -12,44 +13,46 @@ describe('renderer/components/filters/UserHandleFilter.tsx', () => {
1213
jest.clearAllMocks();
1314
});
1415

15-
it('should render itself & its children - detailed notifications enabled', () => {
16-
const tree = render(
17-
<AppContext.Provider
18-
value={{
19-
settings: {
20-
...mockSettings,
21-
detailedNotifications: true,
22-
} as SettingsState,
23-
notifications: [],
24-
}}
25-
>
26-
<MemoryRouter>
27-
<UserHandleFilter />
28-
</MemoryRouter>
29-
</AppContext.Provider>,
30-
);
31-
32-
expect(tree).toMatchSnapshot();
33-
});
16+
describe('should render itself & its children', () => {
17+
it('with detailed notifications enabled', () => {
18+
const tree = render(
19+
<AppContext.Provider
20+
value={{
21+
settings: {
22+
...mockSettings,
23+
detailedNotifications: true,
24+
} as SettingsState,
25+
notifications: mockAccountNotifications,
26+
}}
27+
>
28+
<MemoryRouter>
29+
<UserHandleFilter />
30+
</MemoryRouter>
31+
</AppContext.Provider>,
32+
);
33+
34+
expect(tree).toMatchSnapshot();
35+
});
3436

35-
it('should render itself & its children - detailed notifications disabled', () => {
36-
const tree = render(
37-
<AppContext.Provider
38-
value={{
39-
settings: {
40-
...mockSettings,
41-
detailedNotifications: false,
42-
} as SettingsState,
43-
notifications: [],
44-
}}
45-
>
46-
<MemoryRouter>
47-
<UserHandleFilter />
48-
</MemoryRouter>
49-
</AppContext.Provider>,
50-
);
51-
52-
expect(tree).toMatchSnapshot();
37+
it('with detailed notifications disabled', () => {
38+
const tree = render(
39+
<AppContext.Provider
40+
value={{
41+
settings: {
42+
...mockSettings,
43+
detailedNotifications: false,
44+
} as SettingsState,
45+
notifications: mockAccountNotifications,
46+
}}
47+
>
48+
<MemoryRouter>
49+
<UserHandleFilter />
50+
</MemoryRouter>
51+
</AppContext.Provider>,
52+
);
53+
54+
expect(tree).toMatchSnapshot();
55+
});
5356
});
5457

5558
describe('Include user handles', () => {

src/renderer/components/filters/UserTypeFilter.test.tsx

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { act, fireEvent, render, screen } from '@testing-library/react';
22
import { MemoryRouter } from 'react-router-dom';
3+
import { mockAccountNotifications } from '../../__mocks__/notifications-mocks';
34
import { mockSettings } from '../../__mocks__/state-mocks';
45
import { AppContext } from '../../context/App';
56
import type { SettingsState } from '../../types';
@@ -8,44 +9,46 @@ import { UserTypeFilter } from './UserTypeFilter';
89
describe('renderer/components/filters/UserTypeFilter.tsx', () => {
910
const updateFilter = jest.fn();
1011

11-
it('should render itself & its children - detailed notifications enabled', () => {
12-
const tree = render(
13-
<AppContext.Provider
14-
value={{
15-
settings: {
16-
...mockSettings,
17-
detailedNotifications: true,
18-
} as SettingsState,
19-
notifications: [],
20-
}}
21-
>
22-
<MemoryRouter>
23-
<UserTypeFilter />
24-
</MemoryRouter>
25-
</AppContext.Provider>,
26-
);
12+
describe('should render itself & its children', () => {
13+
it('with detailed notifications enabled', () => {
14+
const tree = render(
15+
<AppContext.Provider
16+
value={{
17+
settings: {
18+
...mockSettings,
19+
detailedNotifications: true,
20+
} as SettingsState,
21+
notifications: mockAccountNotifications,
22+
}}
23+
>
24+
<MemoryRouter>
25+
<UserTypeFilter />
26+
</MemoryRouter>
27+
</AppContext.Provider>,
28+
);
2729

28-
expect(tree).toMatchSnapshot();
29-
});
30+
expect(tree).toMatchSnapshot();
31+
});
3032

31-
it('should render itself & its children - detailed notifications disabled', () => {
32-
const tree = render(
33-
<AppContext.Provider
34-
value={{
35-
settings: {
36-
...mockSettings,
37-
detailedNotifications: false,
38-
} as SettingsState,
39-
notifications: [],
40-
}}
41-
>
42-
<MemoryRouter>
43-
<UserTypeFilter />
44-
</MemoryRouter>
45-
</AppContext.Provider>,
46-
);
33+
it('with detailed notifications disabled', () => {
34+
const tree = render(
35+
<AppContext.Provider
36+
value={{
37+
settings: {
38+
...mockSettings,
39+
detailedNotifications: false,
40+
} as SettingsState,
41+
notifications: mockAccountNotifications,
42+
}}
43+
>
44+
<MemoryRouter>
45+
<UserTypeFilter />
46+
</MemoryRouter>
47+
</AppContext.Provider>,
48+
);
4749

48-
expect(tree).toMatchSnapshot();
50+
expect(tree).toMatchSnapshot();
51+
});
4952
});
5053

5154
it('should be able to toggle user type - none already set', async () => {

0 commit comments

Comments
 (0)