From 6aa1556a6d3e89d4f25f34e3da5e179a68f4c0b7 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Tue, 14 Jan 2025 14:08:01 +0000 Subject: [PATCH] feat: exclusive 'or' checklist with grouped options (#4116) --- .../components/Checklist/Editor/Editor.tsx | 18 +- .../components/Checklist/Editor/Options.tsx | 237 ++++-------------- .../components/ExclusiveOrOptionManager.tsx | 85 +++++++ .../Editor/components/GroupedOptions.tsx | 154 ++++++++++++ .../Editor/{ => components}/OptionsEditor.tsx | 0 .../components/Checklist/Public/Public.tsx | 155 +----------- .../Checklist/Public/components/Checklist.tsx | 132 ++++++++++ .../Public/components/GroupedChecklist.tsx | 129 ++++++++++ .../components/Checklist/Public/helpers.ts | 60 ++++- .../Public/hooks/useExclusiveOption.tsx | 24 +- .../Public/hooks/useInitialOptions.tsx | 19 ++ .../Checklist/Public/tests/Public.test.tsx | 50 +++- .../Checklist/Public/tests/mockOptions.ts | 44 ++++ .../src/@planx/components/Checklist/model.ts | 53 ++-- .../src/@planx/components/Checklist/types.ts | 2 +- .../components/shared/BaseOptionsEditor.tsx | 5 +- .../src/pages/FlowEditor/lib/store/editor.ts | 8 +- .../src/ui/shared/ErrorWrapper.tsx | 2 +- 18 files changed, 800 insertions(+), 377 deletions(-) create mode 100644 editor.planx.uk/src/@planx/components/Checklist/Editor/components/ExclusiveOrOptionManager.tsx create mode 100644 editor.planx.uk/src/@planx/components/Checklist/Editor/components/GroupedOptions.tsx rename editor.planx.uk/src/@planx/components/Checklist/Editor/{ => components}/OptionsEditor.tsx (100%) create mode 100644 editor.planx.uk/src/@planx/components/Checklist/Public/components/Checklist.tsx create mode 100644 editor.planx.uk/src/@planx/components/Checklist/Public/components/GroupedChecklist.tsx create mode 100644 editor.planx.uk/src/@planx/components/Checklist/Public/hooks/useInitialOptions.tsx diff --git a/editor.planx.uk/src/@planx/components/Checklist/Editor/Editor.tsx b/editor.planx.uk/src/@planx/components/Checklist/Editor/Editor.tsx index a197a89170..07aec4b9a0 100644 --- a/editor.planx.uk/src/@planx/components/Checklist/Editor/Editor.tsx +++ b/editor.planx.uk/src/@planx/components/Checklist/Editor/Editor.tsx @@ -1,4 +1,5 @@ import { ComponentType as TYPES } from "@opensystemslab/planx-core/types"; +import { DataFieldAutocomplete } from "@planx/components/shared/DataFieldAutocomplete"; import { FormikErrors, FormikValues, useFormik } from "formik"; import React, { useEffect, useRef } from "react"; import ImgInput from "ui/editor/ImgInput/ImgInput"; @@ -12,7 +13,6 @@ import Input from "ui/shared/Input/Input"; import InputRow from "ui/shared/InputRow"; import { Switch } from "ui/shared/Switch"; -import { DataFieldAutocomplete } from "@planx/components/shared/DataFieldAutocomplete"; import { Option, parseBaseNodeData } from "../../shared"; import { ICONS } from "../../shared/icons"; import type { Checklist } from "../model"; @@ -41,7 +41,7 @@ export const ChecklistEditor: React.FC = (props) => { : groupedOptions?.flatMap((group) => group.children); const filteredOptions = (sourceOptions || []).filter( - (option) => option.data.text + (option) => option.data.text, ); const processedOptions = filteredOptions.map((option) => ({ @@ -68,7 +68,7 @@ export const ChecklistEditor: React.FC = (props) => { }), }, }, - processedOptions + processedOptions, ); } else { alert(JSON.stringify({ type, ...values, options }, null, 2)); @@ -77,16 +77,16 @@ export const ChecklistEditor: React.FC = (props) => { validate: ({ options, groupedOptions, allRequired, ...values }) => { const errors: FormikErrors = {}; + // Account for flat or expandable Checklist options + options = options || groupedOptions?.flatMap((group) => group.children); + const exclusiveOptions: Option[] | undefined = options?.filter( - (option) => option.data.exclusive + (option) => option.data.exclusive, ); if (allRequired && exclusiveOptions && exclusiveOptions.length > 0) { errors.allRequired = 'Cannot configure exclusive "or" option alongside "all required" setting'; } - // Account for flat or expandable Checklist options - options = - options || groupedOptions?.map((group) => group.children)?.flat(); if (values.fn && !options?.some((option) => option.data.val)) { errors.fn = "At least one option must set a data value when the checklist has a data field"; @@ -164,7 +164,7 @@ export const ChecklistEditor: React.FC = (props) => { onChange={() => formik.setFieldValue( "allRequired", - !formik.values.allRequired + !formik.values.allRequired, ) } label="All required" @@ -177,7 +177,7 @@ export const ChecklistEditor: React.FC = (props) => { onChange={() => formik.setFieldValue( "neverAutoAnswer", - !formik.values.neverAutoAnswer + !formik.values.neverAutoAnswer, ) } label="Always put to user (forgo automation)" diff --git a/editor.planx.uk/src/@planx/components/Checklist/Editor/Options.tsx b/editor.planx.uk/src/@planx/components/Checklist/Editor/Options.tsx index 13d2afa804..232f9f7039 100644 --- a/editor.planx.uk/src/@planx/components/Checklist/Editor/Options.tsx +++ b/editor.planx.uk/src/@planx/components/Checklist/Editor/Options.tsx @@ -1,213 +1,74 @@ -import Delete from "@mui/icons-material/Delete"; -import Box from "@mui/material/Box"; -import Button from "@mui/material/Button"; -import IconButton from "@mui/material/IconButton"; -import { BaseOptionsEditor } from "@planx/components/shared/BaseOptionsEditor"; +import { getOptionsSchemaByFn } from "@planx/components/shared/utils"; import { hasFeatureFlag } from "lib/featureFlags"; import { partition } from "lodash"; -import adjust from "ramda/src/adjust"; -import compose from "ramda/src/compose"; -import remove from "ramda/src/remove"; import React from "react"; import { FormikHookReturn } from "types"; import ListManager from "ui/editor/ListManager/ListManager"; import ModalSectionContent from "ui/editor/ModalSectionContent"; -import ErrorWrapper from "ui/shared/ErrorWrapper"; -import Input from "ui/shared/Input/Input"; -import InputRow from "ui/shared/InputRow"; -import { getOptionsSchemaByFn } from "@planx/components/shared/utils"; -import { useStore } from "pages/FlowEditor/lib/store"; import { Option } from "../../shared"; -import type { Group } from "../model"; -import ChecklistOptionsEditor from "./OptionsEditor"; +import { useInitialOptions } from "../Public/hooks/useInitialOptions"; +import { ExclusiveOrOptionManager } from "./components/ExclusiveOrOptionManager"; +import { GroupedOptions } from "./components/GroupedOptions"; +import ChecklistOptionsEditor from "./components/OptionsEditor"; export const Options: React.FC<{ formik: FormikHookReturn }> = ({ formik }) => { const [exclusiveOptions, nonExclusiveOptions]: Option[][] = partition( formik.values.options, - (option) => option.data.exclusive + (option) => option.data.exclusive, ); const exclusiveOrOptionManagerShouldRender = - hasFeatureFlag("EXCLUSIVE_OR") && nonExclusiveOptions.length; - - const schema = useStore().getFlowSchema()?.options; - const initialOptions: Option[] | undefined = formik.initialValues.options || formik.initialValues.groupedOptions?.map((group: Group