From 05dd1244500449ef5de45403e70739d164c96acb Mon Sep 17 00:00:00 2001 From: Julian Roeland Date: Thu, 23 Jan 2025 15:32:33 +0100 Subject: [PATCH] :sparkles: #635 - feat: method to clear filters --- .../e2e/issues/test_635_filters_reset.py | 33 +++++++++++++++++++ .../openarchiefbeheer/utils/tests/gherkin.py | 4 +++ frontend/src/hooks/useFields.ts | 2 ++ .../destructionlist/abstract/BaseListView.tsx | 31 +++++++++++++++-- 4 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 backend/src/openarchiefbeheer/destruction/tests/e2e/issues/test_635_filters_reset.py diff --git a/backend/src/openarchiefbeheer/destruction/tests/e2e/issues/test_635_filters_reset.py b/backend/src/openarchiefbeheer/destruction/tests/e2e/issues/test_635_filters_reset.py new file mode 100644 index 000000000..d148a9a26 --- /dev/null +++ b/backend/src/openarchiefbeheer/destruction/tests/e2e/issues/test_635_filters_reset.py @@ -0,0 +1,33 @@ +# fmt: off +from django.test import tag + +from openarchiefbeheer.destruction.constants import ( + DestructionListItemAction, + InternalStatus, + ListStatus, +) +from openarchiefbeheer.utils.tests.e2e import browser_page +from openarchiefbeheer.utils.tests.gherkin import GherkinLikeTestCase + + +@tag("e2e") +@tag("gh-635") +class Issue635FiltersReset(GherkinLikeTestCase): + async def test_scenario_reset_button_works(self): + async with browser_page() as page: + zaken = await self.given.zaken_are_indexed(amount=6) + record_manager = await self.given.record_manager_exists() + + await self.given.list_exists( + name="Destruction list to reset filters for", + status=ListStatus.ready_to_review, + zaken=zaken, + ) + + await self.when.user_logs_in(page, record_manager) + await self.then.path_should_be(page, "/destruction-lists") + await self.when.user_clicks_button(page, "Destruction list to reset filters for") + await self.when.user_filters_zaken(page, "omschrijving", "some text") + await self.then.url_should_contain_text(page, "omschrijving__icontains=") + await self.when.user_clicks_button(page, "Filters wissen") + await self.then.input_field_should_be_empty(page, "Omschrijving") diff --git a/backend/src/openarchiefbeheer/utils/tests/gherkin.py b/backend/src/openarchiefbeheer/utils/tests/gherkin.py index 9707bab52..0e6cb39a5 100644 --- a/backend/src/openarchiefbeheer/utils/tests/gherkin.py +++ b/backend/src/openarchiefbeheer/utils/tests/gherkin.py @@ -771,3 +771,7 @@ async def this_number_of_zaken_should_be_visible(self, page, number): rows = await locator.locator("tbody").locator("tr").all() self.testcase.assertEqual(len(rows), number) + + async def input_field_should_be_empty(self, page, placeholder): + locator = page.get_by_placeholder(placeholder) + await expect(locator).to_have_value("") diff --git a/frontend/src/hooks/useFields.ts b/frontend/src/hooks/useFields.ts index f8b154de9..e30d8fcde 100644 --- a/frontend/src/hooks/useFields.ts +++ b/frontend/src/hooks/useFields.ts @@ -96,6 +96,7 @@ export function useFields( { name: "startdatum", type: "daterange", + filterLookup: "startdatum__gte,startdatum__lte", filterValue: searchParams.get("startdatum__gte") && searchParams.get("startdatum__lte") @@ -108,6 +109,7 @@ export function useFields( { name: "einddatum", type: "daterange", + filterLookup: "startdatum__gte,startdatum__lte", filterValue: searchParams.get("einddatum__gte") && searchParams.get("einddatum__lte") ? `${searchParams.get("einddatum__gte")}/${searchParams.get("einddatum__lte")}` diff --git a/frontend/src/pages/destructionlist/abstract/BaseListView.tsx b/frontend/src/pages/destructionlist/abstract/BaseListView.tsx index cd7a3e9f3..679215d0b 100644 --- a/frontend/src/pages/destructionlist/abstract/BaseListView.tsx +++ b/frontend/src/pages/destructionlist/abstract/BaseListView.tsx @@ -8,7 +8,7 @@ import { formatMessage, } from "@maykin-ui/admin-ui"; import React, { useCallback, useMemo } from "react"; -import { useNavigation } from "react-router-dom"; +import { useNavigation, useSearchParams } from "react-router-dom"; import { DestructionListToolbar } from "../../../components"; import { @@ -90,6 +90,7 @@ export function BaseListView({ onSelectionChange, }: BaseListViewProps) { const { state } = useNavigation(); + const [searchParams, setSearchParams] = useSearchParams(); const [page, setPage] = usePage(); const [sort, setSort] = useSort(); @@ -108,7 +109,7 @@ export function BaseListView({ type FilterTransformData = ReturnType; // Filter. - const [, setFilterField] = useFilter(); + const [setFilterField] = useFilter(); // Selection. const [ @@ -153,6 +154,19 @@ export function BaseListView({ onClearZaakSelection?.(); }; + const resetFilters = () => { + const filterLookupValues = fields + .filter((field) => field.filterLookup) + .map((field) => field.filterLookup) + // We perform a `flatMap` to handle multiple filter lookups within one field + .flatMap((filterLookup) => filterLookup?.split(",")); + const newSearchParams = new URLSearchParams(searchParams); + filterLookupValues.forEach((filterLookup) => { + if (!filterLookup) return; + newSearchParams.delete(filterLookup); + }); + setSearchParams(newSearchParams); + }; // Selection actions. const getSelectionActions = useCallback(() => { const disabled = selectable && hasSelection; @@ -176,7 +190,18 @@ export function BaseListView({ }, ] as ButtonProps[]) : []; - return [...dynamicItems, ...fixedItems]; + const clearFiltersItem = { + children: ( + <> + + Filters wissen + + ), + variant: "warning", + wrap: false, + onClick: resetFilters, + }; + return [...dynamicItems, ...fixedItems, clearFiltersItem]; }, [selectable, hasSelection, selectedZakenOnPage, selectionActions]); return (