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 2fcc03c3de..3ba495ce53 100644 --- a/editor.planx.uk/src/@planx/components/Checklist/Editor/Editor.tsx +++ b/editor.planx.uk/src/@planx/components/Checklist/Editor/Editor.tsx @@ -7,18 +7,19 @@ import { ModalFooter } from "ui/editor/ModalFooter"; import ModalSection from "ui/editor/ModalSection"; import ModalSectionContent from "ui/editor/ModalSectionContent"; import RichTextInput from "ui/editor/RichTextInput/RichTextInput"; +import ErrorWrapper from "ui/shared/ErrorWrapper"; import Input from "ui/shared/Input/Input"; import InputRow from "ui/shared/InputRow"; import { Switch } from "ui/shared/Switch"; -import { parseBaseNodeData } from "../../shared"; +import { Option, parseBaseNodeData } from "../../shared"; import { ICONS } from "../../shared/icons"; import type { Checklist } from "../model"; import { toggleExpandableChecklist } from "../model"; import { ChecklistProps } from "../types"; import { Options } from "./Options"; -export const ChecklistComponent: React.FC = (props) => { +export const ChecklistEditor: React.FC = (props) => { const type = TYPES.Checklist; const formik = useFormik({ @@ -39,7 +40,7 @@ export const ChecklistComponent: 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) => ({ @@ -66,14 +67,22 @@ export const ChecklistComponent: React.FC = (props) => { }), }, }, - processedOptions, + processedOptions ); } else { alert(JSON.stringify({ type, ...values, options }, null, 2)); } }, - validate: ({ options, groupedOptions, ...values }) => { + validate: ({ options, groupedOptions, allRequired, ...values }) => { const errors: FormikErrors = {}; + + const exclusiveOptions: Option[] | undefined = options?.filter( + (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(); @@ -81,6 +90,10 @@ export const ChecklistComponent: React.FC = (props) => { errors.fn = "At least one option must set a data value when the checklist has a data field"; } + if (exclusiveOptions && exclusiveOptions.length > 1) { + errors.options = + "There should be a maximum of one exclusive option configured"; + } return errors; }, }); @@ -160,19 +173,20 @@ export const ChecklistComponent: React.FC = (props) => { onChange={() => formik.setFieldValue( "allRequired", - !formik.values.allRequired, + !formik.values.allRequired ) } label="All required" /> + formik.setFieldValue( "neverAutoAnswer", - !formik.values.neverAutoAnswer, + !formik.values.neverAutoAnswer ) } label="Always put to user (forgo automation)" @@ -180,8 +194,9 @@ export const ChecklistComponent: React.FC = (props) => { - - + + + @@ -189,4 +204,4 @@ export const ChecklistComponent: React.FC = (props) => { ); }; -export default ChecklistComponent; +export default ChecklistEditor; 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 388c021dac..c5fa9ee02b 100644 --- a/editor.planx.uk/src/@planx/components/Checklist/Editor/Options.tsx +++ b/editor.planx.uk/src/@planx/components/Checklist/Editor/Options.tsx @@ -2,6 +2,9 @@ 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 { 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"; @@ -9,6 +12,7 @@ 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"; @@ -17,6 +21,14 @@ import type { Group } from "../model"; import ChecklistOptionsEditor from "./OptionsEditor"; export const Options: React.FC<{ formik: FormikHookReturn }> = ({ formik }) => { + const [exclusiveOptions, nonExclusiveOptions]: Option[][] = partition( + formik.values.options, + (option) => option.data.exclusive + ); + + const exclusiveOrOptionManagerShouldRender = + hasFeatureFlag("EXCLUSIVE_OR") && nonExclusiveOptions.length; + return ( {formik.values.groupedOptions ? ( @@ -42,7 +54,7 @@ export const Options: React.FC<{ formik: FormikHookReturn }> = ({ formik }) => { onClick={() => { formik.setFieldValue( `groupedOptions`, - remove(groupIndex, 1, formik.values.groupedOptions), + remove(groupIndex, 1, formik.values.groupedOptions) ); }} size="large" @@ -57,7 +69,7 @@ export const Options: React.FC<{ formik: FormikHookReturn }> = ({ formik }) => { onChange={(newOptions) => { formik.setFieldValue( `groupedOptions[${groupIndex}].children`, - newOptions, + newOptions ); }} newValue={() => @@ -76,7 +88,7 @@ export const Options: React.FC<{ formik: FormikHookReturn }> = ({ formik }) => { showValueField: !!formik.values.fn, onMoveToGroup: ( movedItemIndex: number, - moveToGroupIndex: number, + moveToGroupIndex: number ) => { const item = groupedOption.children[movedItemIndex]; formik.setFieldValue( @@ -87,27 +99,27 @@ export const Options: React.FC<{ formik: FormikHookReturn }> = ({ formik }) => { (option: Group