Skip to content

Commit e9aaa4b

Browse files
committed
feat: add state filter
Signed-off-by: Adam Setch <[email protected]>
1 parent 4603fef commit e9aaa4b

File tree

11 files changed

+2155
-2
lines changed

11 files changed

+2155
-2
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

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { act, fireEvent, render, screen } from '@testing-library/react';
2+
import { MemoryRouter } from 'react-router-dom';
3+
import { mockSettings } from '../../__mocks__/state-mocks';
4+
import { AppContext } from '../../context/App';
5+
import type { SettingsState } from '../../types';
6+
import { StateFilter } from './StateFilter';
7+
8+
describe('renderer/components/filters/StateFilter.tsx', () => {
9+
const updateFilter = jest.fn();
10+
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+
<StateFilter />
24+
</MemoryRouter>
25+
</AppContext.Provider>,
26+
);
27+
28+
expect(tree).toMatchSnapshot();
29+
});
30+
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+
<StateFilter />
44+
</MemoryRouter>
45+
</AppContext.Provider>,
46+
);
47+
48+
expect(tree).toMatchSnapshot();
49+
});
50+
51+
it('should be able to toggle user type - none already set', async () => {
52+
await act(async () => {
53+
render(
54+
<AppContext.Provider
55+
value={{
56+
settings: {
57+
...mockSettings,
58+
filterStates: [],
59+
},
60+
notifications: [],
61+
updateFilter,
62+
}}
63+
>
64+
<MemoryRouter>
65+
<StateFilter />
66+
</MemoryRouter>
67+
</AppContext.Provider>,
68+
);
69+
});
70+
71+
fireEvent.click(screen.getByLabelText('Open'));
72+
73+
expect(updateFilter).toHaveBeenCalledWith('filterStates', 'open', true);
74+
75+
expect(
76+
screen.getByLabelText('Open').parentNode.parentNode,
77+
).toMatchSnapshot();
78+
});
79+
80+
it('should be able to toggle user type - some filters already set', async () => {
81+
await act(async () => {
82+
render(
83+
<AppContext.Provider
84+
value={{
85+
settings: {
86+
...mockSettings,
87+
filterStates: ['open'],
88+
},
89+
notifications: [],
90+
updateFilter,
91+
}}
92+
>
93+
<MemoryRouter>
94+
<StateFilter />
95+
</MemoryRouter>
96+
</AppContext.Provider>,
97+
);
98+
});
99+
100+
fireEvent.click(screen.getByLabelText('Closed'));
101+
102+
expect(updateFilter).toHaveBeenCalledWith('filterStates', 'closed', true);
103+
104+
expect(
105+
screen.getByLabelText('Closed').parentNode.parentNode,
106+
).toMatchSnapshot();
107+
});
108+
});
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+
};

0 commit comments

Comments
 (0)