Skip to content

Commit

Permalink
refactor filters & questions
Browse files Browse the repository at this point in the history
  • Loading branch information
jessicamcinchak committed Jan 5, 2025
1 parent 6db8501 commit 6d2d075
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 122 deletions.
2 changes: 2 additions & 0 deletions editor.planx.uk/src/@planx/components/Filter/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ComponentType as TYPES,
DEFAULT_FLAG_CATEGORY,
flatFlags,
Node,
} from "@opensystemslab/planx-core/types";
import { useFormik } from "formik";
import React from "react";
Expand All @@ -15,6 +16,7 @@ export interface Props {
id?: string;
handleSubmit?: (data: any, children?: any) => void;
node?: any;
autoAnswer?: Node["id"];
}

const Filter: React.FC<Props> = (props) => {
Expand Down
10 changes: 2 additions & 8 deletions editor.planx.uk/src/@planx/components/Filter/Public.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { useStore } from "pages/FlowEditor/lib/store";
import { useEffect } from "react";

import { PublicProps } from "../shared/types";
Expand All @@ -8,17 +7,12 @@ export type Props = PublicProps<Filter>;

// Filters are always auto-answered and never seen by a user, but should still leave a breadcrumb
export default function Component(props: Props) {
const autoAnswerableFlag = useStore((state) => state.autoAnswerableFlag);

let idThatCanBeAutoAnswered: string | undefined;
if (props.id) idThatCanBeAutoAnswered = autoAnswerableFlag(props.id);

useEffect(() => {
props.handleSubmit?.({
answers: [idThatCanBeAutoAnswered],
answers: [props.autoAnswer],
auto: true,
});
}, []);
}, [props.autoAnswer]);

return null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import FormLabel from "@mui/material/FormLabel";
import Grid from "@mui/material/Grid";
import RadioGroup from "@mui/material/RadioGroup";
import { visuallyHidden } from "@mui/utils";
import { Edges } from "@opensystemslab/planx-core/types";
import Card from "@planx/components/shared/Preview/Card";
import { CardHeader } from "@planx/components/shared/Preview/CardHeader/CardHeader";
import BasicRadio from "@planx/components/shared/Radio/BasicRadio";
Expand All @@ -17,7 +16,7 @@ import FullWidthWrapper from "ui/public/FullWidthWrapper";
import ErrorWrapper from "ui/shared/ErrorWrapper";
import { mixed, object, string } from "yup";

import { Question } from "../model";
import { Question } from "./model";

export enum QuestionLayout {
Basic,
Expand All @@ -26,48 +25,11 @@ export enum QuestionLayout {
}

const QuestionComponent: React.FC<Question> = (props) => {
const [flow, autoAnswerableOptions] = useStore((state) => [
state.flow,
state.autoAnswerableOptions,
]);
// Questions without edges act like "sticky notes" in the graph for editors only & are auto-answered
const flow = useStore().flow;
const edges = props.id ? flow[props.id]?.edges : undefined;
const isStickyNote = !edges || edges.length === 0;

if (props.neverAutoAnswer) {
return <VisibleQuestion {...props} />;
}

// Questions without edges act like "sticky notes" in the graph for editors only & can be immediately auto-answered
let edges: Edges | undefined;
if (props.id) edges = flow[props.id]?.edges;
if (!edges || edges.length === 0) {
return <AutoAnsweredQuestion {...props} answerIds={undefined} />;
}

let idsThatCanBeAutoAnswered: string[] | undefined;
if (props.id) idsThatCanBeAutoAnswered = autoAnswerableOptions(props.id);
if (idsThatCanBeAutoAnswered) {
return (
<AutoAnsweredQuestion {...props} answerIds={idsThatCanBeAutoAnswered} />
);
}

return <VisibleQuestion {...props} />;
};

// An auto-answered Question won't be seen by the user, but still leaves a breadcrumb
const AutoAnsweredQuestion: React.FC<
Question & { answerIds: string[] | undefined }
> = (props) => {
useEffect(() => {
props.handleSubmit?.({
answers: props.answerIds,
auto: true,
});
}, []);

return null;
};

const VisibleQuestion: React.FC<Question> = (props) => {
const previousResponseId = props?.previouslySubmittedData?.answers?.[0];
const previousResponseKey = props.responses.find(
(response) => response.id === previousResponseId,
Expand Down Expand Up @@ -104,6 +66,20 @@ const VisibleQuestion: React.FC<Question> = (props) => {
layout = QuestionLayout.Descriptions;
}

useEffect(() => {
if (isStickyNote || props.autoAnswers) {
props.handleSubmit?.({
answers: props.autoAnswers,
auto: true,
});
}
}, [isStickyNote, props.autoAnswers]);

// Auto-answered questions are not publicly visible
if (isStickyNote || props.autoAnswers) {
return null;
}

return (
<Card handleSubmit={formik.handleSubmit}>
<CardHeader
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Meta, StoryObj } from "@storybook/react";

import Question from "./Question";
import Question from "./Public";

const meta = {
title: "PlanX Components/Question",
Expand Down
1 change: 1 addition & 0 deletions editor.planx.uk/src/@planx/components/Question/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ export interface Question extends BaseNodeData {
}[];
previouslySubmittedData?: Store.UserData;
handleSubmit: HandleSubmit;
autoAnswers?: string[] | undefined;
}
97 changes: 58 additions & 39 deletions editor.planx.uk/src/pages/Preview/Node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,18 @@ interface Props {
}

const Node: React.FC<Props> = (props) => {
const [childNodesOf, resetPreview, cachedBreadcrumbs] = useStore((state) => [
const [
childNodesOf,
resetPreview,
cachedBreadcrumbs,
autoAnswerableFlag,
autoAnswerableOptions,
] = useStore((state) => [
state.childNodesOf,
state.resetPreview,
state.cachedBreadcrumbs,
state.autoAnswerableFlag,
state.autoAnswerableOptions,
]);

const handleSubmit = props.handleSubmit;
Expand All @@ -96,13 +104,16 @@ const Node: React.FC<Props> = (props) => {
});

switch (props.node.type) {
case TYPES.AddressInput:
return <AddressInputComponent {...getComponentProps<AddressInput>()} />;

case TYPES.Calculate:
return <CalculateComponent {...getComponentProps<Calculate>()} />;

case TYPES.Checklist: {
const checklistProps = getComponentProps<Checklist>();
const childNodes = childNodesOf(
props.node.id,
nodeId,
) as (typeof checklistProps)["options"];

return (
Expand Down Expand Up @@ -135,6 +146,9 @@ const Node: React.FC<Props> = (props) => {
case TYPES.Confirmation:
return <ConfirmationComponent {...getComponentProps<Confirmation>()} />;

case TYPES.ContactInput:
return <ContactInputComponent {...getComponentProps<ContactInput>()} />;

case TYPES.Content:
return <ContentComponent {...getComponentProps<Content>()} />;

Expand All @@ -143,8 +157,10 @@ const Node: React.FC<Props> = (props) => {

case TYPES.DrawBoundary:
return <DrawBoundaryComponent {...getComponentProps<DrawBoundary>()} />;

case TYPES.Feedback:
return <FeedbackComponent {...getComponentProps<Feedback>()} />;

case TYPES.FileUpload:
return <FileUploadComponent {...getComponentProps<FileUpload>()} />;

Expand All @@ -155,6 +171,13 @@ const Node: React.FC<Props> = (props) => {
/>
);

case TYPES.Filter: {
const filterProps = getComponentProps<Filter>();
const autoAnswer = nodeId ? autoAnswerableFlag(nodeId) : undefined;

return <FilterComponent {...filterProps} autoAnswer={autoAnswer} />;
}

case TYPES.FindProperty:
return <FindPropertyComponent {...getComponentProps<FindProperty>()} />;

Expand All @@ -179,6 +202,38 @@ const Node: React.FC<Props> = (props) => {
case TYPES.Pay:
return <PayComponent {...getComponentProps<Pay>()} />;

case TYPES.PlanningConstraints:
return (
<PlanningConstraintsComponent
{...getComponentProps<PlanningConstraints>()}
/>
);

case TYPES.PropertyInformation:
return (
<PropertyInformationComponent
{...getComponentProps<PropertyInformation>()}
/>
);

case TYPES.Question: {
const questionProps = getComponentProps<Question>();
const autoAnswers = nodeId ? autoAnswerableOptions(nodeId) : undefined;

return (
<QuestionComponent
{...questionProps}
responses={childNodesOf(nodeId).map((n, i) => ({
id: n.id,
responseKey: i + 1,
title: n.data?.text,
...n.data,
}))}
autoAnswers={autoAnswers}
/>
);
}

case TYPES.Result:
return <ResultComponent {...getComponentProps<Result>()} />;

Expand All @@ -194,19 +249,6 @@ const Node: React.FC<Props> = (props) => {
case TYPES.SetValue:
return <SetValueComponent {...getComponentProps<SetValue>()} />;

case TYPES.Question:
return (
<QuestionComponent
{...getComponentProps<Question>()}
responses={childNodesOf(props.node.id).map((n, i) => ({
id: n.id,
responseKey: i + 1,
title: n.data?.text,
...n.data,
}))}
/>
);

case TYPES.TaskList: {
const taskListProps = getComponentProps<TaskList>();

Expand All @@ -221,34 +263,11 @@ const Node: React.FC<Props> = (props) => {
case TYPES.TextInput:
return <TextInputComponent {...getComponentProps<TextInput>()} />;

case TYPES.AddressInput:
return <AddressInputComponent {...getComponentProps<AddressInput>()} />;

case TYPES.ContactInput:
return <ContactInputComponent {...getComponentProps<ContactInput>()} />;

case TYPES.PlanningConstraints:
return (
<PlanningConstraintsComponent
{...getComponentProps<PlanningConstraints>()}
/>
);

case TYPES.PropertyInformation:
return (
<PropertyInformationComponent
{...getComponentProps<PropertyInformation>()}
/>
);

case TYPES.Filter:
return <FilterComponent {...getComponentProps<Filter>()} />;

// These types are never seen by users, nor do they leave their own breadcrumbs entry
case TYPES.Answer:
case TYPES.ExternalPortal:
case TYPES.Flow:
case TYPES.InternalPortal:
case TYPES.Answer:
case undefined:
return null;

Expand Down

0 comments on commit 6d2d075

Please sign in to comment.