Skip to content

Commit 709ecb5

Browse files
Julian RoelandXaohs
Julian Roeland
authored andcommitted
#635 - feat: method to clear filters
1 parent 93ff844 commit 709ecb5

File tree

4 files changed

+130
-12
lines changed

4 files changed

+130
-12
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# fmt: off
2+
from django.test import tag
3+
4+
from openarchiefbeheer.destruction.constants import ListStatus
5+
from openarchiefbeheer.utils.tests.e2e import browser_page
6+
from openarchiefbeheer.utils.tests.gherkin import GherkinLikeTestCase
7+
8+
9+
@tag("e2e")
10+
@tag("gh-635")
11+
class Issue635FiltersReset(GherkinLikeTestCase):
12+
# Tests if:
13+
# - Reset button resets query parameters
14+
# - Reset button resets input fields
15+
# - Reset button resets page number to 1
16+
# - Reset button is not shown when no filters are applied
17+
async def test_scenario_reset_button_works(self):
18+
async with browser_page() as page:
19+
zaken = await self.given.zaken_are_indexed(amount=500)
20+
record_manager = await self.given.record_manager_exists()
21+
22+
await self.given.list_exists(
23+
name="Destruction list to reset filters for",
24+
status=ListStatus.ready_to_review,
25+
zaken=zaken,
26+
)
27+
28+
await self.when.user_logs_in(page, record_manager)
29+
await self.then.path_should_be(page, "/destruction-lists")
30+
await self.when.user_clicks_button(page, "Destruction list to reset filters for")
31+
await self.then.url_should_contain_text(page, "destruction-lists/")
32+
initial_url_with_page = page.url + "?page=1"
33+
await self.when.user_clicks_button(page, "Volgende")
34+
await self.then.url_should_contain_text(page, "page=2")
35+
await self.then.page_should_not_contain_text(page, "Filters wissen")
36+
await self.when.user_filters_zaken(page, "omschrijving", "some text")
37+
await self.then.url_should_contain_text(page, "omschrijving__icontains=")
38+
await self.when.user_clicks_button(page, "Filters wissen")
39+
await self.then.input_field_should_be_empty(page, "Omschrijving")
40+
await self.then.url_should_be(page, initial_url_with_page)

backend/src/openarchiefbeheer/utils/tests/gherkin.py

+12
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,14 @@ async def page_should_contain_text(self, page, text, timeout=None):
723723
element = page.locator(f"text={text}")
724724
await expect(element.nth(0)).to_be_visible(timeout=timeout)
725725

726+
async def page_should_not_contain_text(self, page, text, timeout=None):
727+
if timeout is None:
728+
timeout = 500 if self.is_inverted else 10000
729+
730+
# Check if the text is not present within the timeout
731+
element = page.locator(f"text={text}")
732+
await expect(element).to_have_count(0, timeout=timeout)
733+
726734
async def page_should_contain_element_with_title(
727735
self, page, title, timeout=5000
728736
):
@@ -771,3 +779,7 @@ async def this_number_of_zaken_should_be_visible(self, page, number):
771779
rows = await locator.locator("tbody").locator("tr").all()
772780

773781
self.testcase.assertEqual(len(rows), number)
782+
783+
async def input_field_should_be_empty(self, page, placeholder):
784+
locator = page.get_by_placeholder(placeholder)
785+
await expect(locator).to_have_value("")

frontend/src/hooks/useFields.ts

+55-4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ export function useFields<T extends Zaak = Zaak>(
4545
(
4646
filterData: Partial<TypedSerializedFormData<keyof T & string>>,
4747
) => FilterTransformReturnType<T>,
48+
Record<string, string>,
49+
() => void,
4850
] {
4951
const [fieldSelectionState, setFieldSelectionState] =
5052
useState<FieldSelection>();
@@ -53,7 +55,7 @@ export function useFields<T extends Zaak = Zaak>(
5355
setFieldSelectionState(fieldSelection),
5456
);
5557
}, []);
56-
const [searchParams] = useSearchParams();
58+
const [searchParams, setSearchParams] = useSearchParams();
5759
const selectielijstKlasseChoices = useSelectielijstKlasseChoices();
5860
const zaaktypeChoices = useZaaktypeChoices(
5961
destructionList,
@@ -62,8 +64,9 @@ export function useFields<T extends Zaak = Zaak>(
6264
);
6365

6466
// The raw, unfiltered configuration of the available base fields.
67+
// Both filterLookup AND filterLookups will be used for clearing filters.
6568
// NOTE: This get filtered by `getActiveFields()`.
66-
const fields: TypedField<T>[] = [
69+
const fields: (TypedField<T> & { filterLookups?: string[] })[] = [
6770
{
6871
name: "identificatie",
6972
filterLookup: "identificatie__icontains",
@@ -96,6 +99,7 @@ export function useFields<T extends Zaak = Zaak>(
9699
{
97100
name: "startdatum",
98101
type: "daterange",
102+
filterLookups: ["startdatum__gte", "startdatum__lte"],
99103
filterValue:
100104
searchParams.get("startdatum__gte") &&
101105
searchParams.get("startdatum__lte")
@@ -108,6 +112,7 @@ export function useFields<T extends Zaak = Zaak>(
108112
{
109113
name: "einddatum",
110114
type: "daterange",
115+
filterLookups: ["einddatum__gte", "einddatum__lte"],
111116
filterValue:
112117
searchParams.get("einddatum__gte") && searchParams.get("einddatum__lte")
113118
? `${searchParams.get("einddatum__gte")}/${searchParams.get("einddatum__lte")}`
@@ -162,6 +167,7 @@ export function useFields<T extends Zaak = Zaak>(
162167
name: "archiefactiedatum",
163168
type: "daterange",
164169
width: "130px",
170+
filterLookups: ["archiefactiedatum__gte", "archiefactiedatum__lte"],
165171
filterValue:
166172
searchParams.get("archiefactiedatum__gte") &&
167173
searchParams.get("archiefactiedatum__lte")
@@ -206,6 +212,17 @@ export function useFields<T extends Zaak = Zaak>(
206212
...(extraFields || []),
207213
];
208214

215+
const filterLookupValues = [
216+
...new Set(
217+
fields
218+
.flatMap((field) => [
219+
field.filterLookup,
220+
...(field.filterLookups || []),
221+
])
222+
.filter(Boolean),
223+
),
224+
];
225+
209226
const getActiveFields = useCallback(() => {
210227
return fields.map((field) => {
211228
const isActiveFromStorage =
@@ -214,10 +231,38 @@ export function useFields<T extends Zaak = Zaak>(
214231
typeof isActiveFromStorage === "undefined"
215232
? field.active !== false
216233
: isActiveFromStorage;
217-
return { ...field, active: isActive } as TypedField;
234+
return { ...field, active: isActive };
218235
});
219236
}, [fields, fieldSelectionState]);
220237

238+
/**
239+
* Function to reset all the filters
240+
* It will concat all the `filterLookup` and `filterLookups` values from the `fields` array and remove them from the searchParams
241+
*/
242+
const resetFilters = () => {
243+
const newSearchParams = new URLSearchParams(searchParams);
244+
filterLookupValues.forEach((filterLookup) => {
245+
if (!filterLookup) return;
246+
newSearchParams.delete(filterLookup);
247+
});
248+
setSearchParams(newSearchParams);
249+
};
250+
251+
/**
252+
* A function to return the current active filters
253+
*/
254+
const getActiveFilters = () => {
255+
const activeFilters: Record<string, string> = {};
256+
filterLookupValues.forEach((filterLookup) => {
257+
if (!filterLookup) return;
258+
const value = searchParams.get(filterLookup);
259+
if (value) {
260+
activeFilters[filterLookup] = value;
261+
}
262+
});
263+
return activeFilters;
264+
};
265+
221266
/**
222267
* Gets called when the fields selection is changed.
223268
* Pass this to `filterTransform` of a DataGrid component.
@@ -267,5 +312,11 @@ export function useFields<T extends Zaak = Zaak>(
267312
};
268313
};
269314

270-
return [getActiveFields(), setFields, filterTransform];
315+
return [
316+
getActiveFields(),
317+
setFields,
318+
filterTransform,
319+
getActiveFilters(),
320+
resetFilters,
321+
];
271322
}

frontend/src/pages/destructionlist/abstract/BaseListView.tsx

+23-8
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,8 @@ export function BaseListView<T extends Zaak = Zaak>({
100100
})) as unknown as T[];
101101

102102
// Fields.
103-
const [fields, setFields, filterTransform] = useFields<T>(
104-
destructionList,
105-
review,
106-
extraFields,
107-
);
103+
const [fields, setFields, filterTransform, activeFilters, resetFilters] =
104+
useFields<T>(destructionList, review, extraFields);
108105
type FilterTransformData = ReturnType<typeof filterTransform>;
109106

110107
// Filter.
@@ -162,7 +159,7 @@ export function BaseListView<T extends Zaak = Zaak>({
162159
: { ...props, disabled: selectable && !hasSelection },
163160
);
164161
const fixedItems = disabled
165-
? ([
162+
? [
166163
{
167164
children: (
168165
<>
@@ -174,9 +171,27 @@ export function BaseListView<T extends Zaak = Zaak>({
174171
wrap: false,
175172
onClick: handleClearZaakSelection,
176173
},
177-
] as ButtonProps[])
174+
]
178175
: [];
179-
return [...dynamicItems, ...fixedItems];
176+
if (!Object.keys(activeFilters).length) {
177+
return [...dynamicItems, ...fixedItems];
178+
}
179+
180+
return [
181+
...dynamicItems,
182+
...fixedItems,
183+
{
184+
children: (
185+
<>
186+
<Solid.XCircleIcon />
187+
Filters wissen
188+
</>
189+
),
190+
variant: "warning",
191+
wrap: false,
192+
onClick: resetFilters,
193+
},
194+
];
180195
}, [selectable, hasSelection, selectedZakenOnPage, selectionActions]);
181196

182197
return (

0 commit comments

Comments
 (0)