Skip to content

Commit

Permalink
Merge branch 'main' of github.com:theopensystemslab/planx-new into je…
Browse files Browse the repository at this point in the history
…ss/selectable-planning-constraints
  • Loading branch information
jessicamcinchak committed Nov 28, 2024
2 parents 65e699e + 7a523e9 commit fefc6a9
Show file tree
Hide file tree
Showing 24 changed files with 512 additions and 79 deletions.
10 changes: 4 additions & 6 deletions api.planx.uk/modules/flows/validate/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,12 @@ export const hasComponentType = (
const nodeIds = Object.entries(flowGraph).filter(
(entry): entry is [string, Node] => isComponentType(entry, type),
);

if (fn) {
nodeIds
?.filter(([_nodeId, nodeData]) => nodeData?.data?.fn === fn)
?.map(([nodeId, _nodeData]) => nodeId);
} else {
nodeIds?.map(([nodeId, _nodeData]) => nodeId);
return nodeIds.some(([, nodeData]) => nodeData?.data?.fn === fn);
}
return Boolean(nodeIds?.length);

return Boolean(nodeIds.length);
};

export const numberOfComponentType = (
Expand Down
12 changes: 3 additions & 9 deletions api.planx.uk/modules/flows/validate/validate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -419,15 +419,9 @@ describe("invite to pay validation on diff", () => {
});

it("does not update if invite to pay is enabled, but there is not a Checklist that sets `proposal.projectType`", async () => {
const {
Checklist: _Checklist,
ChecklistOptionOne: _ChecklistOptionOne,
ChecklistOptionTwo: _ChecklistOptionTwo,
...invalidatedFlow
} = flowWithInviteToPay;
invalidatedFlow["_root"].edges?.splice(
invalidatedFlow["_root"].edges?.indexOf("Checklist"),
);
const invalidatedFlow = flowWithInviteToPay;
// Remove proposal.projectType, set incorrect variable
invalidatedFlow!.Checklist!.data!.fn = "some.other.variable";

queryMock.mockQuery({
name: "GetFlowData",
Expand Down
90 changes: 90 additions & 0 deletions doc/how-to/how-to-add-fields-to-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# How to add fields to the Editor search index

## Overview

This guide outlines the process of adding new searchable fields to the PlanX Editors' frontend search functionality, which uses Fuse.js for indexing and searching. This is a required step with adding a new component to PlanX, or adding new fields to an existing component.

## Background

- Search is currently implemented in the frontend using Fuse.js
- Only certain fields are searchable:
- Text fields (simple text)
- Rich text fields (HTML)
- Data values (e.g. `data.fn`)

## Process

### 1. Update facets configuration

Location: `src/pages/FlowEditor/components/Sidebar/Search/facets.ts`

#### Guidelines:
- Use key paths to the new fields (e.g. `data.myNewField`)
- Wrap rich text fields with `richTextField()` helper - this strips HTML tags
- Add data fields to `DATA_FACETS`
- Add text fields to `ALL_FACETS`
- Avoid adding duplicate values already held in `ALL_FACETS` (e.g., `data.title`, `data.description`)

#### Example:

```ts
// facets.ts

const myNewComponent: SearchFacets = [
richTextField("data.myRichTextField"),
"data.myPlainTextField"
];

export const ALL_FACETS: SearchFacets = [
...otherComponents,
...myNewComponent,
...DATA_FACETS,
];
```

### 2. Assign display values

Location: `src/pages/FlowEditor/components/Sidebar/Search/SearchResultCard/getDisplayDetailsForResult.tsx`

#### Add key formatters:

```ts
// getDisplayDetailsForResult.tsx

const keyFormatters: KeyMap = {
...existingFormatters,
"data.myNewField": {
getDisplayKey: () => "My New Field",
},
};
```

### 3. Update tests

Locations:
- `src/pages/FlowEditor/components/Sidebar/Search/SearchResultCard/allFacets.test.ts`
- `src/pages/FlowEditor/components/Sidebar/Search/SearchResultCard/dataFacets.test.ts`

#### Test steps:
1. Add new component to `mockFlow`
2. Create mock search result
- Example: `mockMyComponentResult: SearchResult<IndexedNode>`

### Debugging tip

A search result can be easily logged to the console from `SearchResultCard`. Simply search for one of your new fields, and click on the card.

```ts
// SearchResultCard/index.tsx

const handleClick = () => {
const url = getURLForNode(result.item.id);
// Temporarily disable navigation
// navigate(url);

// Log the full search result to console
console.log({ result });
};
```

For reference, [please see this PR](https://github.com/theopensystemslab/planx-new/pull/4015) which added the text fields for the `Feedback` component to the search index.
6 changes: 6 additions & 0 deletions e2e/tests/ui-driven/src/create-flow.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ test.describe("Flow creation, publish and preview", () => {
await editor.createResult();
await editor.createNextSteps();
await editor.createReview();
await editor.createFeedback();
await editor.createConfirmation();

await expect(editor.nodeList).toContainText([
Expand All @@ -91,6 +92,7 @@ test.describe("Flow creation, publish and preview", () => {
"Planning permission", // default result flag
"Next steps",
"Check your answers before sending your application",
"Tell us what you think",
"Confirmation",
]);
});
Expand Down Expand Up @@ -290,6 +292,10 @@ test.describe("Flow creation, publish and preview", () => {
).toBeVisible();
await clickContinue({ page });

await expect(
page.locator("h1", { hasText: "Tell us what you think" }),
).toBeVisible();
await clickContinue({ page });
await expect(
page.locator("h1", { hasText: "Application sent" }),
).toBeVisible();
Expand Down
14 changes: 11 additions & 3 deletions e2e/tests/ui-driven/src/helpers/addComponent.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ComponentType } from "@opensystemslab/planx-core/types";
import { Locator, Page } from "@playwright/test";
import { expect, Locator, Page } from "@playwright/test";

const createBaseComponent = async (
page: Page,
Expand All @@ -10,7 +10,10 @@ const createBaseComponent = async (
) => {
await locatingNode.click();
await page.getByRole("dialog").waitFor();
await page.locator("select").selectOption({ value: type.toString() });
const headerSelect = page.getByRole("heading", { name: "Question close" });
await headerSelect.locator("select").selectOption({ value: type.toString() });

await expect(page.getByTestId("header-select")).toHaveValue(type.toString());

switch (type) {
case ComponentType.Question:
Expand All @@ -23,7 +26,7 @@ const createBaseComponent = async (
await page.getByPlaceholder("Notice").fill(title || "");
break;
case ComponentType.Checklist:
await page.getByPlaceholder("Text").fill(title || "");
await page.getByPlaceholder("Text").fill(title || "text");
if (options) {
await createComponentOptions(options, "add new option", page);
}
Expand Down Expand Up @@ -114,6 +117,7 @@ const createBaseComponent = async (
.fill(options?.[0] || "");
break;
case ComponentType.Filter:
case ComponentType.Feedback:
break;
case ComponentType.InternalPortal:
await page.getByPlaceholder("Portal name").fill(title || "");
Expand Down Expand Up @@ -386,3 +390,7 @@ export const createInternalPortal = async (
portalName,
);
};

export const createFeedback = async (page: Page, locatingNode: Locator) => {
await createBaseComponent(page, locatingNode, ComponentType.Feedback);
};
5 changes: 5 additions & 0 deletions e2e/tests/ui-driven/src/pages/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
createContent,
createDateInput,
createDrawBoundary,
createFeedback,
createFileUpload,
createFilter,
createFindProperty,
Expand Down Expand Up @@ -240,4 +241,8 @@ export class PlaywrightEditor {
.fill("A notice inside a portal!");
await this.page.locator('button[form="modal"][type="submit"]').click();
}

async createFeedback() {
await createFeedback(this.page, this.getNextNode());
}
}
4 changes: 4 additions & 0 deletions editor.planx.uk/docs/adding-a-new-component.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ import SetValue from "@planx/components/SetValue/Editor";
set: SetValueComponent,
```

6. Add text fields to search index

Please see detailed guide here - https://github.com/theopensystemslab/planx-new/blob/main/doc/how-to/how-to-add-fields-to-search.md

## Preview environment & integrations

1. `src/pages/Preview/Node.tsx`
Expand Down
23 changes: 7 additions & 16 deletions editor.planx.uk/src/@planx/components/Pay/Editor/Editor.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
import {
ComponentType as TYPES,
} from "@opensystemslab/planx-core/types";
import {
parsePay,
Pay,
validationSchema,
} from "@planx/components/Pay/model";
import { ComponentType as TYPES } from "@opensystemslab/planx-core/types";
import { parsePay, Pay, validationSchema } from "@planx/components/Pay/model";
import { Form, Formik } from "formik";
import React from "react";
import { ComponentTagSelect } from "ui/editor/ComponentTagSelect";
Expand All @@ -20,13 +14,13 @@ import { Switch } from "ui/shared/Switch";

import { ICONS } from "../../shared/icons";
import { EditorProps } from "../../shared/types";
import { FeeBreakdownSection } from "./FeeBreakdownSection";
import { GovPayMetadataSection } from "./GovPayMetadataSection";
import { InviteToPaySection } from "./InviteToPaySection";

export type Props = EditorProps<TYPES.Pay, Pay>;

const Component: React.FC<Props> = (props: Props) => {

const onSubmit = (newValues: Pay) => {
if (props.handleSubmit) {
props.handleSubmit({ type: TYPES.Pay, data: newValues });
Expand All @@ -41,11 +35,7 @@ const Component: React.FC<Props> = (props: Props) => {
validateOnChange={true}
validateOnBlur={true}
>
{({
values,
handleChange,
setFieldValue,
}) => (
{({ values, handleChange, setFieldValue }) => (
<Form id="modal" name="modal">
<ModalSection>
<ModalSectionContent title="Payment" Icon={ICONS[TYPES.Pay]}>
Expand Down Expand Up @@ -112,8 +102,9 @@ const Component: React.FC<Props> = (props: Props) => {
</InputRow>
</ModalSectionContent>
</ModalSection>
<GovPayMetadataSection/>
<InviteToPaySection/>
<GovPayMetadataSection />
<InviteToPaySection />
<FeeBreakdownSection />
<MoreInformation
changeField={handleChange}
definitionImg={values.definitionImg}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import ReceiptLongIcon from "@mui/icons-material/ReceiptLong";
import { useFormikContext } from "formik";
import { hasFeatureFlag } from "lib/featureFlags";
import React from "react";
import ModalSection from "ui/editor/ModalSection";
import ModalSectionContent from "ui/editor/ModalSectionContent";
import InputRow from "ui/shared/InputRow";
import { Switch } from "ui/shared/Switch";

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

export const FeeBreakdownSection: React.FC = () => {
const { values, setFieldValue } = useFormikContext<Pay>();

if (!hasFeatureFlag("FEE_BREAKDOWN")) return null;

return (
<ModalSection>
<ModalSectionContent title="Fee breakdown" Icon={ReceiptLongIcon}>
<InputRow>
<Switch
checked={values.showFeeBreakdown}
onChange={() =>
setFieldValue("showFeeBreakdown", !values.showFeeBreakdown)
}
label="Display a breakdown of the fee to the applicant"
/>
</InputRow>
</ModalSectionContent>
</ModalSection>
);
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import React from "react";
import Box from "@mui/material/Box";
import {
ComponentType as TYPES,
} from "@opensystemslab/planx-core/types";
import { ComponentType as TYPES } from "@opensystemslab/planx-core/types";
import { useFormikContext } from "formik";
import React from "react";
import ModalSection from "ui/editor/ModalSection";
import ModalSectionContent from "ui/editor/ModalSectionContent";
import RichTextInput from "ui/editor/RichTextInput/RichTextInput";
Expand All @@ -11,7 +10,6 @@ import InputRow from "ui/shared/InputRow";
import { Switch } from "ui/shared/Switch";

import { ICONS } from "../../shared/icons";
import { useFormikContext } from "formik";
import { Pay } from "../model";

export const InviteToPaySection: React.FC = () => {
Expand Down Expand Up @@ -98,5 +96,5 @@ export const InviteToPaySection: React.FC = () => {
)}
</ModalSectionContent>
</ModalSection>
)
}
);
};
23 changes: 9 additions & 14 deletions editor.planx.uk/src/@planx/components/Pay/Public/Confirm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,24 @@ import FormWrapper from "ui/public/FormWrapper";
import ErrorSummary from "ui/shared/ErrorSummary/ErrorSummary";
import ReactMarkdownOrHtml from "ui/shared/ReactMarkdownOrHtml/ReactMarkdownOrHtml";

import { formattedPriceWithCurrencySymbol, getDefaultContent } from "../model";
import {
formattedPriceWithCurrencySymbol,
getDefaultContent,
Pay,
} from "../model";
import { FeeBreakdown } from "./FeeBreakdown";
import InviteToPayForm, { InviteToPayFormProps } from "./InviteToPayForm";
import { PAY_API_ERROR_UNSUPPORTED_TEAM } from "./Pay";

export interface Props {
export interface Props extends Omit<Pay, "title" | "fn" | "govPayMetadata"> {
title?: string;
bannerTitle?: string;
description?: string;
fee: number;
instructionsTitle?: string;
instructionsDescription?: string;
showInviteToPay?: boolean;
secondaryPageTitle?: string;
nomineeTitle?: string;
nomineeDescription?: string;
yourDetailsTitle?: string;
yourDetailsDescription?: string;
yourDetailsLabel?: string;
paymentStatus?: PaymentStatus;
buttonTitle?: string;
onConfirm: () => void;
error?: string;
hideFeeBanner?: boolean;
hidePay?: boolean;
}

interface PayBodyProps extends Props {
Expand Down Expand Up @@ -86,7 +80,7 @@ const PayBody: React.FC<PayBodyProps> = (props) => {
<Card>
<PayText>
<Typography variant="h2" component={props.hideFeeBanner ? "h2" : "h3"}>
{props.instructionsTitle || defaults.instructionsTitle }
{props.instructionsTitle || defaults.instructionsTitle}
</Typography>
<ReactMarkdownOrHtml
source={
Expand Down Expand Up @@ -187,6 +181,7 @@ export default function Confirm(props: Props) {
/>
</Typography>
</FormWrapper>
{props.showFeeBreakdown && <FeeBreakdown />}
</Banner>
)}
{page === "Pay" ? (
Expand Down
Loading

0 comments on commit fefc6a9

Please sign in to comment.