Skip to content

Commit

Permalink
Add exclusive-or select input and validation
Browse files Browse the repository at this point in the history
  • Loading branch information
jamdelion committed Dec 9, 2024
1 parent 688bff6 commit 0683fd5
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const ChecklistComponent: React.FC<ChecklistProps> = (props) => {
img: props.node?.data?.img || "",
options: props.options,
text: props.node?.data?.text || "",
exclusiveOrOption: props.exclusiveOrOption || undefined,
...parseBaseNodeData(props.node?.data),
},
onSubmit: ({ options, groupedOptions, ...values }) => {
Expand Down
31 changes: 31 additions & 0 deletions editor.planx.uk/src/@planx/components/Checklist/Editor/Options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ 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 MenuItem from "@mui/material/MenuItem";
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 SelectInput from "ui/editor/SelectInput/SelectInput";
import Input from "ui/shared/Input/Input";
import InputRow from "ui/shared/InputRow";
import InputRowItem from "ui/shared/InputRowItem";
import InputRowLabel from "ui/shared/InputRowLabel";

import { Option } from "../../shared";
import type { Group } from "../model";
Expand Down Expand Up @@ -146,6 +150,33 @@ export const Options: React.FC<{ formik: FormikHookReturn }> = ({ formik }) => {
editorExtraProps={{ showValueField: !!formik.values.fn }}
/>
)}
{/* TODO: only show if more than one option */}
{formik.values.options && (
<Box pt={2}>
<InputRow>
<InputRowLabel>Exclusive or option</InputRowLabel>
<InputRowItem>
<SelectInput
name="exclusiveOrOption"
onChange={(e) => {
formik.setFieldValue("exclusiveOrOption", e.target.value);
// TODO: make it take effect
}}
value={formik.values.exclusiveOrOption || ""}
>
{formik.values?.options?.map((option: Option) => {
const optionTitle = option?.data?.text;
return (
<MenuItem key={optionTitle} value={optionTitle}>
{optionTitle}
</MenuItem>
);
})}
</SelectInput>
</InputRowItem>
</InputRow>
</Box>
)}
</ModalSectionContent>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const VisibleChecklist: React.FC<Props> = (props) => {
img,
previouslySubmittedData,
id,
exclusiveOrOption,
} = props;

const formik = useFormik<{ checked: Array<string> }>({
Expand Down
29 changes: 26 additions & 3 deletions editor.planx.uk/src/@planx/components/Checklist/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export interface Checklist extends BaseNodeData {
allRequired?: boolean;
categories?: Array<Category>;
neverAutoAnswer?: boolean;
exclusiveOrOption?: string;
}

interface ChecklistExpandableProps {
Expand Down Expand Up @@ -102,8 +103,11 @@ export const checklistValidationSchema = ({
allRequired,
options,
groupedOptions,
}: Checklist) =>
array()
exclusiveOrOption,
}: Checklist) => {
const flatOptions = getFlatOptions({ options, groupedOptions });

return array()
.required()
.test({
name: "atLeastOneChecked",
Expand All @@ -119,8 +123,27 @@ export const checklistValidationSchema = ({
if (!allRequired) {
return true;
}
const flatOptions = getFlatOptions({ options, groupedOptions });
const allChecked = checked && checked.length === flatOptions.length;
return Boolean(allChecked);
},
})
.test({
name: "notExclusiveOrSelection",
message: `Cannot select "${exclusiveOrOption}" alongside other options`,
// TODO: something more like 'Select countries you will be travelling to, or select ‘No, I will not be travelling to any of these countries’
test: (checked?: Array<string>) => {
const multipleSelectedOptionsIncludesExclusiveOr =
exclusiveOrOption &&
flatOptions.some((option) => {
const optionHasBeenPicked = checked?.includes(option.id);
return (
checked &&
optionHasBeenPicked &&
checked.length > 1 &&
option.data.text === exclusiveOrOption
);
});
return !multipleSelectedOptionsIncludesExclusiveOr;
},
});
};
1 change: 1 addition & 0 deletions editor.planx.uk/src/@planx/components/Checklist/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface ChecklistProps extends Checklist {
fn?: string;
img?: string;
text: string;
exclusiveOrOption?: Option;
} & BaseNodeData;
};
}
Expand Down

0 comments on commit 0683fd5

Please sign in to comment.