From ca78e839cf7bab9eb1ef02e11bcaf572d5c16f24 Mon Sep 17 00:00:00 2001 From: Jessica McInchak Date: Fri, 11 Oct 2024 12:46:49 +0200 Subject: [PATCH] feat: Filters support choosing any flagset category (#3797) --- .../src/create-flow-with-geospatial.spec.ts | 2 +- .../@planx/components/Filter/Editor.test.tsx | 141 ++++++++++++++++++ .../src/@planx/components/Filter/Editor.tsx | 90 +++++++---- .../src/@planx/components/Filter/model.ts | 5 - .../components/Flow/components/Node.tsx | 12 +- 5 files changed, 213 insertions(+), 37 deletions(-) create mode 100644 editor.planx.uk/src/@planx/components/Filter/Editor.test.tsx delete mode 100644 editor.planx.uk/src/@planx/components/Filter/model.ts diff --git a/e2e/tests/ui-driven/src/create-flow-with-geospatial.spec.ts b/e2e/tests/ui-driven/src/create-flow-with-geospatial.spec.ts index 691f83a257..991dcd0bc5 100644 --- a/e2e/tests/ui-driven/src/create-flow-with-geospatial.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow-with-geospatial.spec.ts @@ -62,7 +62,7 @@ test.describe("Flow creation, publish and preview", () => { await expect(editor.nodeList).toContainText([ "Find property", "an internal portalEdit Portal", - "(Flags Filter)ImmuneMissing informationPermission neededPrior approvalNoticePermitted developmentNot development(No Result)", + "Filter - Planning permissionImmuneMissing informationPermission neededPrior approvalNoticePermitted developmentNot developmentNo flag result", "Upload and label", "Confirm your location plan", "Planning constraints", diff --git a/editor.planx.uk/src/@planx/components/Filter/Editor.test.tsx b/editor.planx.uk/src/@planx/components/Filter/Editor.test.tsx new file mode 100644 index 0000000000..950d00b272 --- /dev/null +++ b/editor.planx.uk/src/@planx/components/Filter/Editor.test.tsx @@ -0,0 +1,141 @@ +import { + ComponentType as TYPES, + DEFAULT_FLAG_CATEGORY, + flatFlags, +} from "@opensystemslab/planx-core/types"; +import { fireEvent, screen, waitFor } from "@testing-library/react"; +import React from "react"; +import { setup } from "testUtils"; +import { vi } from "vitest"; + +import Filter from "./Editor"; + +test("Adding a filter without explicit props uses the default flagset", async () => { + const handleSubmit = vi.fn(); + + setup(); + + expect(screen.getByTestId("flagset-category-select")).toHaveValue( + DEFAULT_FLAG_CATEGORY, + ); + + fireEvent.submit(screen.getByTestId("filter-component-form")); + + await waitFor(() => + expect(handleSubmit).toHaveBeenCalledWith( + { + type: TYPES.Filter, + data: { + fn: "flag", + category: DEFAULT_FLAG_CATEGORY, + }, + }, + mockDefaultFlagOptions, + ), + ); +}); + +test("Adding a filter and selecting a flagset category", async () => { + const handleSubmit = vi.fn(); + + setup(); + + expect(screen.getByTestId("flagset-category-select")).toHaveValue( + DEFAULT_FLAG_CATEGORY, + ); + + fireEvent.change(screen.getByTestId("flagset-category-select"), { + target: { value: "Community infrastructure levy" }, + }); + + fireEvent.submit(screen.getByTestId("filter-component-form")); + + await waitFor(() => + expect(handleSubmit).toHaveBeenCalledWith( + { + type: TYPES.Filter, + data: { + fn: "flag", + category: "Community infrastructure levy", + }, + }, + mockCILFlagOptions, + ), + ); +}); + +test("Updating an existing filter to another category", async () => { + const handleSubmit = vi.fn(); + + setup( + , + ); + + expect(screen.getByTestId("flagset-category-select")).toHaveValue( + "Listed building consent", + ); + + fireEvent.change(screen.getByTestId("flagset-category-select"), { + target: { value: "Community infrastructure levy" }, + }); + + fireEvent.submit(screen.getByTestId("filter-component-form")); + + await waitFor(() => + expect(handleSubmit).toHaveBeenCalledWith( + { + type: TYPES.Filter, + data: { + fn: "flag", + category: "Community infrastructure levy", + }, + }, + mockCILFlagOptions, + ), + ); +}); + +const mockExistingFilterNode = { + data: { + fn: "flag", + category: "Listed building consent", + }, + type: 500, + edges: ["flag1", "flag2", "flag3", "flag4", "blankFlag"], +}; + +const mockDefaultFlagOptions = [ + ...flatFlags.filter((flag) => flag.category === DEFAULT_FLAG_CATEGORY), + { + category: DEFAULT_FLAG_CATEGORY, + text: "No flag result", + value: "", + }, +].map((flag) => ({ + type: TYPES.Answer, + data: { + text: flag.text, + val: flag.value, + }, +})); + +const mockCILFlagOptions = [ + ...flatFlags.filter( + (flag) => flag.category === "Community infrastructure levy", + ), + { + category: "Community infrastructure levy", + text: "No flag result", + value: "", + }, +].map((flag) => ({ + type: TYPES.Answer, + data: { + text: flag.text, + val: flag.value, + }, +})); diff --git a/editor.planx.uk/src/@planx/components/Filter/Editor.tsx b/editor.planx.uk/src/@planx/components/Filter/Editor.tsx index 5d2f3f2b03..b7b84f8ec3 100644 --- a/editor.planx.uk/src/@planx/components/Filter/Editor.tsx +++ b/editor.planx.uk/src/@planx/components/Filter/Editor.tsx @@ -1,52 +1,84 @@ +import Typography from "@mui/material/Typography"; import { + ComponentType as TYPES, DEFAULT_FLAG_CATEGORY, flatFlags, } from "@opensystemslab/planx-core/types"; -import { ComponentType as TYPES } from "@opensystemslab/planx-core/types"; import { useFormik } from "formik"; import React from "react"; +import ModalSection from "ui/editor/ModalSection"; +import ModalSectionContent from "ui/editor/ModalSectionContent"; + +import { ICONS } from "../ui"; export interface Props { id?: string; - handleSubmit?: (d: any, c?: any) => void; + handleSubmit?: (data: any, children?: any) => void; node?: any; } const Filter: React.FC = (props) => { const formik = useFormik({ - initialValues: {}, + initialValues: { + fn: "flag", + category: props?.node?.data?.category || DEFAULT_FLAG_CATEGORY, + }, onSubmit: (newValues) => { - if (props.handleSubmit) { - const children = props.id - ? undefined - : [ - ...flatFlags, - { - category: DEFAULT_FLAG_CATEGORY, - text: "(No Result)", - value: "", - }, - ] - .filter((f) => f.category === DEFAULT_FLAG_CATEGORY) - .map((f) => ({ - type: TYPES.Answer, - data: { - text: f.text, - val: f.value, - }, - })); + if (props?.handleSubmit) { + const children = [ + ...flatFlags, + { + category: formik.values.category, + text: "No flag result", + value: "", + }, + ] + .filter((f) => f.category === formik.values.category) + .map((f) => ({ + type: TYPES.Answer, + data: { + text: f.text, + val: f.value, + }, + })); - props.handleSubmit( - { type: TYPES.Filter, data: { newValues, fn: "flag" } }, - children, - ); + props.handleSubmit({ type: TYPES.Filter, data: newValues }, children); } }, - validate: () => {}, }); + + const categories = new Set(flatFlags.map((flag) => flag.category)); + return ( - ); }; diff --git a/editor.planx.uk/src/@planx/components/Filter/model.ts b/editor.planx.uk/src/@planx/components/Filter/model.ts deleted file mode 100644 index 0ca9cf9275..0000000000 --- a/editor.planx.uk/src/@planx/components/Filter/model.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface Filter { - id?: string; - handleSubmit?: (d: any, children: any) => void; - node?: any; -} diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Flow/components/Node.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Flow/components/Node.tsx index 2537272cb3..4a043a42a7 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Flow/components/Node.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Flow/components/Node.tsx @@ -1,4 +1,7 @@ -import { ComponentType as TYPES } from "@opensystemslab/planx-core/types"; +import { + ComponentType as TYPES, + DEFAULT_FLAG_CATEGORY, +} from "@opensystemslab/planx-core/types"; import React from "react"; import { ErrorBoundary } from "react-error-boundary"; import { exhaustiveCheck } from "utils"; @@ -63,7 +66,12 @@ const Node: React.FC = (props) => { /> ); case TYPES.Filter: - return ; + return ( + + ); case TYPES.FindProperty: return ; case TYPES.List: