From 812b88e703ccd9ff0adea531ef731e920507355e Mon Sep 17 00:00:00 2001 From: Jessica McInchak Date: Tue, 21 Nov 2023 09:13:18 +0100 Subject: [PATCH 1/4] add toggle to optionally format calculate output for automations --- .../src/@planx/components/Calculate/Editor.tsx | 12 ++++++++++++ .../src/@planx/components/Calculate/Public/index.tsx | 5 +++-- .../src/@planx/components/Calculate/model.ts | 4 +++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/editor.planx.uk/src/@planx/components/Calculate/Editor.tsx b/editor.planx.uk/src/@planx/components/Calculate/Editor.tsx index 431820a98e..59d3cfe698 100644 --- a/editor.planx.uk/src/@planx/components/Calculate/Editor.tsx +++ b/editor.planx.uk/src/@planx/components/Calculate/Editor.tsx @@ -14,6 +14,7 @@ import InputGroup from "ui/InputGroup"; import InputRow from "ui/InputRow"; import ModalSection from "ui/ModalSection"; import ModalSectionContent from "ui/ModalSectionContent"; +import OptionButton from "ui/OptionButton"; import type { Calculate } from "./model"; import { evaluate, getVariables, parseCalculate } from "./model"; @@ -85,6 +86,17 @@ export default function Component(props: Props) { onChange={formik.handleChange} /> + { + formik.setFieldValue( + "formatOutputForAutomations", + !formik.values.formatOutputForAutomations, + ); + }} + > + Format the output to automate a future Question or Checklist only + diff --git a/editor.planx.uk/src/@planx/components/Calculate/Public/index.tsx b/editor.planx.uk/src/@planx/components/Calculate/Public/index.tsx index 01a1234522..9373cda103 100644 --- a/editor.planx.uk/src/@planx/components/Calculate/Public/index.tsx +++ b/editor.planx.uk/src/@planx/components/Calculate/Public/index.tsx @@ -12,17 +12,18 @@ export default function Component(props: Props) { const passport = useStore((state) => state.computePassport().data); useEffect(() => { + const evaluatedResult = evaluate(props.formula, passport, props.defaults); props.handleSubmit?.({ ...makeData( props, - evaluate(props.formula, passport, props.defaults), + props.formatOutputForAutomations ? [evaluatedResult.toString()] : evaluatedResult, props.output, ), // don't show this component to the user, auto=true required // for back button to skip past this component when going back auto: true, }); - }, []); + }, [props, passport]); return null; } diff --git a/editor.planx.uk/src/@planx/components/Calculate/model.ts b/editor.planx.uk/src/@planx/components/Calculate/model.ts index 75908d7fc1..1930015a63 100644 --- a/editor.planx.uk/src/@planx/components/Calculate/model.ts +++ b/editor.planx.uk/src/@planx/components/Calculate/model.ts @@ -7,6 +7,7 @@ export interface Calculate extends MoreInformation { defaults: Record; formula: string; samples: Record; + formatOutputForAutomations?: boolean; } export interface Input { @@ -22,6 +23,7 @@ export const parseCalculate = ( defaults: data?.defaults || {}, formula: data?.formula || "", samples: data?.samples || {}, + formatOutputForAutomations: data?.formatOutputForAutomations || false, }); export function getVariables(input: string): Set { @@ -82,7 +84,7 @@ export function evaluate(input: string, scope = {}, defaults = {}): number { } } -// Serialization is only necessary internally. v +// Serialization is only necessary internally. // Mathjs can't handle keys with dots in their names e.g. `property.number` // This complexity should never be exposed to this component's consumers. function serialize(x: string) { From e1f447639116f55325ad18ad1d5bc0bd4b8a123e Mon Sep 17 00:00:00 2001 From: Jessica McInchak Date: Thu, 23 Nov 2023 11:04:59 +0100 Subject: [PATCH 2/4] add tests, simpler file structure --- .../Calculate/Calculate.stories.tsx | 2 +- .../components/Calculate/Public.test.tsx | 23 +++ .../{Public/index.tsx => Public.tsx} | 4 +- .../components/Calculate/logic.test.tsx | 185 ++++++++++++++++++ 4 files changed, 211 insertions(+), 3 deletions(-) create mode 100644 editor.planx.uk/src/@planx/components/Calculate/Public.test.tsx rename editor.planx.uk/src/@planx/components/Calculate/{Public/index.tsx => Public.tsx} (91%) create mode 100644 editor.planx.uk/src/@planx/components/Calculate/logic.test.tsx diff --git a/editor.planx.uk/src/@planx/components/Calculate/Calculate.stories.tsx b/editor.planx.uk/src/@planx/components/Calculate/Calculate.stories.tsx index 0ed9b038c1..bf17244984 100644 --- a/editor.planx.uk/src/@planx/components/Calculate/Calculate.stories.tsx +++ b/editor.planx.uk/src/@planx/components/Calculate/Calculate.stories.tsx @@ -6,7 +6,7 @@ import React from "react"; import Wrapper from "../fixtures/Wrapper"; import { WarningContainer } from "../shared/Preview/WarningContainer"; import Editor from "./Editor"; -import Public from "./Public/index"; +import Public from "./Public"; export default { title: "PlanX Components/Calculate", diff --git a/editor.planx.uk/src/@planx/components/Calculate/Public.test.tsx b/editor.planx.uk/src/@planx/components/Calculate/Public.test.tsx new file mode 100644 index 0000000000..a503780597 --- /dev/null +++ b/editor.planx.uk/src/@planx/components/Calculate/Public.test.tsx @@ -0,0 +1,23 @@ +import React from "react"; +import { setup } from "testUtils"; + +import Public from "./Public"; + +describe("Calculate component", () => { + it("renders correctly", () => { + const handleSubmit = jest.fn(); + setup( + , + ); + + // Calculate should be auto-answered and never shown to user + expect(handleSubmit).toHaveBeenCalled(); + }); +}); diff --git a/editor.planx.uk/src/@planx/components/Calculate/Public/index.tsx b/editor.planx.uk/src/@planx/components/Calculate/Public.tsx similarity index 91% rename from editor.planx.uk/src/@planx/components/Calculate/Public/index.tsx rename to editor.planx.uk/src/@planx/components/Calculate/Public.tsx index 9373cda103..75baa728e7 100644 --- a/editor.planx.uk/src/@planx/components/Calculate/Public/index.tsx +++ b/editor.planx.uk/src/@planx/components/Calculate/Public.tsx @@ -3,8 +3,8 @@ import type { PublicProps } from "@planx/components/ui"; import { useStore } from "pages/FlowEditor/lib/store"; import { useEffect } from "react"; -import type { Calculate } from "../model"; -import { evaluate } from "../model"; +import type { Calculate } from "./model"; +import { evaluate } from "./model"; export type Props = PublicProps; diff --git a/editor.planx.uk/src/@planx/components/Calculate/logic.test.tsx b/editor.planx.uk/src/@planx/components/Calculate/logic.test.tsx new file mode 100644 index 0000000000..81c876d366 --- /dev/null +++ b/editor.planx.uk/src/@planx/components/Calculate/logic.test.tsx @@ -0,0 +1,185 @@ +import { TYPES } from "@planx/components/types"; +import { Store, vanillaStore } from "pages/FlowEditor/lib/store"; + +const { getState, setState } = vanillaStore; +const { upcomingCardIds, resetPreview, record, currentCard } = getState(); + +// Helper method +const visitedNodes = () => Object.keys(getState().breadcrumbs); + +beforeEach(() => { + resetPreview(); +}); + +test("When formatOutputForAutomations is true, Calculate writes an array and future questions are auto-answered", () => { + // Setup + setState({ flow: flowWithAutomation }); + expect(upcomingCardIds()).toEqual([ + "Calculate", + "Question", + ]); + + // Step forwards through the Calculate + record("Calculate", { data: { testGroup: ["2"] }, auto: true }); + upcomingCardIds(); + + // The Question has been auto-answered + expect(visitedNodes()).toEqual([ + "Calculate", + "Question", + ]); + + expect(upcomingCardIds()).toEqual([ + "Group2Notice", + ]); +}); + +test("When formatOutputForAutomations is false, Calculate writes a number and future questions are not auto-answered", () => { + // Setup + setState({ flow: flowWithoutAutomation }); + expect(upcomingCardIds()).toEqual([ + "Calculate", + "Question", + ]); + + // Step forwards through the Calculate + record("Calculate", { data: { testGroup: 2 }, auto: true }); + upcomingCardIds(); + + // The Question has NOT been auto-answered + expect(visitedNodes()).toEqual([ + "Calculate", + ]); + + expect(upcomingCardIds()).toEqual([ + "Question" + ]); +}); + +const flowWithAutomation: Store.flow = { + "_root": { + "edges": [ + "Calculate", + "Question" + ] + }, + "Group2Notice": { + "data": { + "color": "#EFEFEF", + "title": "You are Group 2", + "resetButton": false + }, + "type": TYPES.Notice + }, + "Calculate": { + "data": { + "output": "testGroup", + "formula": "pickRandom([1,2])", + "formatOutputForAutomations": true + }, + "type": TYPES.Calculate + }, + "Group1Notice": { + "data": { + "color": "#EFEFEF", + "title": "You are Group 1", + "resetButton": false + }, + "type": TYPES.Notice + }, + "Group1Response": { + "data": { + "val": "1", + "text": "1" + }, + "type": TYPES.Response, + "edges": [ + "Group1Notice" + ] + }, + "Question": { + "data": { + "fn": "testGroup", + "text": "Which test group? " + }, + "type": TYPES.Statement, + "edges": [ + "Group1Response", + "Group2Response" + ] + }, + "Group2Response": { + "data": { + "val": "2", + "text": "2" + }, + "type": TYPES.Response, + "edges": [ + "Group2Notice" + ] + } +}; + +const flowWithoutAutomation: Store.flow = { + "_root": { + "edges": [ + "Calculate", + "Question" + ] + }, + "Group2Notice": { + "data": { + "color": "#EFEFEF", + "title": "You are Group 2", + "resetButton": false + }, + "type": TYPES.Notice + }, + "Calculate": { + "data": { + "output": "testGroup", + "formula": "pickRandom([1,2])", + "formatOutputForAutomations": false + }, + "type": TYPES.Calculate + }, + "Group1Notice": { + "data": { + "color": "#EFEFEF", + "title": "You are Group 1", + "resetButton": false + }, + "type": TYPES.Notice + }, + "Group1Response": { + "data": { + "val": "1", + "text": "1" + }, + "type": TYPES.Response, + "edges": [ + "Group1Notice" + ] + }, + "Question": { + "data": { + "fn": "testGroup", + "text": "Which test group? " + }, + "type": TYPES.Statement, + "edges": [ + "Group1Response", + "Group2Response" + ] + }, + "Group2Response": { + "data": { + "val": "2", + "text": "2" + }, + "type": TYPES.Response, + "edges": [ + "Group2Notice" + ] + } +}; From 31d02aefcf161fd9217395b6d42214b4f8042e03 Mon Sep 17 00:00:00 2001 From: Jessica McInchak Date: Thu, 23 Nov 2023 11:33:03 +0100 Subject: [PATCH 3/4] fix import --- .../src/@planx/components/Calculate/Public.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/editor.planx.uk/src/@planx/components/Calculate/Public.test.tsx b/editor.planx.uk/src/@planx/components/Calculate/Public.test.tsx index a503780597..997a3180b7 100644 --- a/editor.planx.uk/src/@planx/components/Calculate/Public.test.tsx +++ b/editor.planx.uk/src/@planx/components/Calculate/Public.test.tsx @@ -1,13 +1,13 @@ import React from "react"; import { setup } from "testUtils"; -import Public from "./Public"; +import Calculate from "./Public"; describe("Calculate component", () => { it("renders correctly", () => { const handleSubmit = jest.fn(); setup( - Date: Thu, 23 Nov 2023 11:34:20 +0100 Subject: [PATCH 4/4] logic tests should be ts extension not tsx --- .../@planx/components/Calculate/{logic.test.tsx => logic.test.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename editor.planx.uk/src/@planx/components/Calculate/{logic.test.tsx => logic.test.ts} (100%) diff --git a/editor.planx.uk/src/@planx/components/Calculate/logic.test.tsx b/editor.planx.uk/src/@planx/components/Calculate/logic.test.ts similarity index 100% rename from editor.planx.uk/src/@planx/components/Calculate/logic.test.tsx rename to editor.planx.uk/src/@planx/components/Calculate/logic.test.ts