diff --git a/app/components/Form/ProjectForm.tsx b/app/components/Form/ProjectForm.tsx index b03b9ed517..b1826ee7c5 100644 --- a/app/components/Form/ProjectForm.tsx +++ b/app/components/Form/ProjectForm.tsx @@ -19,7 +19,6 @@ interface Props { } // You only need to include the optional arguments when using this function to create the schema for the summary (read-only) page. export const createProjectUiSchema = ( - tradeName, legalName?, bcRegistryId?, rfpStream?, @@ -38,12 +37,10 @@ export const createProjectUiSchema = ( "projectStatusId", ], proposalReference: { - "ui:placeholder": "2020-RFP-1-123-ABCD", "bcgov:size": "small", - "bcgov:help-text": "(e.g. 2020-RFP-1-ABCD-123)", + "ui:help": (e.g. 2020-RFP-1-ABCD-123), }, projectName: { - "ui:placeholder": "Short project name", "ui:col-md": 12, "bcgov:size": "small", }, @@ -64,16 +61,6 @@ export const createProjectUiSchema = ( "ui:widget": "SearchWidget", "ui:options": { text: `${legalName ? `${legalName} (${bcRegistryId})` : ""}`, - title: "Legal Operator Name and BC Registry ID", - }, - }, - operatorTradeName: { - "ui:col-md": 12, - "ui:widget": "DisplayOnly", - "bcgov:size": "small", - "ui:options": { - text: `${tradeName}`, - title: "Trade Name", }, }, fundingStreamRfpId: { @@ -82,6 +69,7 @@ export const createProjectUiSchema = ( "bcgov:size": "small", "ui:options": { text: `${rfpStream}`, + label: rfpStream ? true : false, }, }, projectStatusId: { @@ -198,17 +186,23 @@ const ProjectForm: React.FC = (props) => { }; const schema: JSONSchema7 = useMemo(() => { - const initialSchema = projectSchema; - initialSchema.properties.operatorId.anyOf = query.allOperators.edges.map( - ({ node }) => { - return { - type: "number", - title: `${node.legalName} (${node.bcRegistryId})`, - enum: [node.rowId], - value: node.rowId, - }; - } - ); + const initialSchema = { + ...projectSchema, + properties: { + ...projectSchema.properties, + operatorId: { + ...projectSchema.properties.operatorId, + anyOf: query.allOperators.edges.map(({ node }) => { + return { + type: "number", + title: `${node.legalName} (${node.bcRegistryId})`, + enum: [node.rowId], + value: node.rowId, + }; + }), + }, + }, + }; return initialSchema as JSONSchema7; }, [query]); diff --git a/app/components/Form/ProjectFormSummary.tsx b/app/components/Form/ProjectFormSummary.tsx index 01e1bd368d..d5b5e226c1 100644 --- a/app/components/Form/ProjectFormSummary.tsx +++ b/app/components/Form/ProjectFormSummary.tsx @@ -46,7 +46,6 @@ const ProjectFormSummary: React.FC = (props) => { node { rowId legalName - tradeName bcRegistryId } } @@ -100,7 +99,6 @@ const ProjectFormSummary: React.FC = (props) => { theme={readOnlyTheme} schema={projectSchema as JSONSchema7} uiSchema={createProjectUiSchema( - selectedOperator ? selectedOperator.node.tradeName : "", selectedOperator ? selectedOperator.node.legalName : "", selectedOperator ? selectedOperator.node.bcRegistryId : "", rfpStream diff --git a/app/components/Form/ProjectManagerForm.tsx b/app/components/Form/ProjectManagerForm.tsx index 04fe41b954..68bbd3bb26 100644 --- a/app/components/Form/ProjectManagerForm.tsx +++ b/app/components/Form/ProjectManagerForm.tsx @@ -32,9 +32,9 @@ export const createProjectManagerUiSchema = (contact?, role?) => { "ui:col-md": 12, "bcgov:size": "small", "ui:widget": "SearchWidget", + "ui:title": role, "ui:options": { - label: false, - title: `${role}`, + label: role ? true : false, text: `${contact}`, }, }, @@ -132,7 +132,6 @@ const ProjectManagerForm: React.FC = (props) => { label={change.projectManagerLabel.label} required={false} htmlFor={`${formIdPrefix}_cifUserId`} - uiSchema={uiSchema} />
= (props) => { - const { - id, - onChange, - placeholder, - label, - required, - uiSchema, - value, - formContext, - } = props; + const { id, onChange, placeholder, required, uiSchema, value, formContext } = + props; const { allFundingStreamRfpProjectStatuses } = useFragment( graphql` @@ -46,7 +38,6 @@ const SelectProjectStatus: React.FunctionComponent = (props) => { return (
- onChange(e.target.value || undefined)} diff --git a/app/cypress/integration/cif/project-revision/index.spec.js b/app/cypress/integration/cif/project-revision/index.spec.js index 3519909c50..2736b1a691 100644 --- a/app/cypress/integration/cif/project-revision/index.spec.js +++ b/app/cypress/integration/cif/project-revision/index.spec.js @@ -77,7 +77,7 @@ describe("the new project page", () => { component: "Project Overview Form", variant: "with errors", }); - cy.get(".error-detail").should("have.length", 6); + cy.get(".error-detail").should("have.length", 7); // Renders the default error message for a required field cy.get(".error-detail").last().should("contain", "Please enter a value"); diff --git a/app/data/jsonSchemaForm/projectSchema.ts b/app/data/jsonSchemaForm/projectSchema.ts index d01c7db551..173983c37b 100644 --- a/app/data/jsonSchemaForm/projectSchema.ts +++ b/app/data/jsonSchemaForm/projectSchema.ts @@ -8,6 +8,7 @@ const projectSchema = { "operatorId", "fundingStreamRfpId", "totalFundingRequest", + "projectStatusId", ], properties: { proposalReference: { @@ -27,9 +28,6 @@ const projectSchema = { default: undefined, anyOf: undefined, }, - operatorTradeName: { - type: "string", - }, fundingStreamRfpId: { type: "number", title: "Funding Stream RFP ID", diff --git a/app/lib/theme/FieldTemplate.tsx b/app/lib/theme/FieldTemplate.tsx index 23667300ce..44afc82301 100644 --- a/app/lib/theme/FieldTemplate.tsx +++ b/app/lib/theme/FieldTemplate.tsx @@ -1,47 +1,60 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faExclamationTriangle } from "@fortawesome/free-solid-svg-icons"; +import { FieldTemplateProps } from "@rjsf/core"; +import FieldLabel from "./widgets/FieldLabel"; -const FieldTemplate = ({ children, errors, help, rawErrors }) => { +const FieldTemplate: React.FC = ({ + children, + errors, + help, + rawErrors, + label, + displayLabel, + required, + id, +}) => { return ( - <> -
- {children} +
+ {displayLabel && ( + + )} + {help} + {children} -
- {rawErrors && rawErrors.length > 0 ? ( - <> - - {errors} - - ) : null} -
- {help} +
+ {rawErrors && rawErrors.length > 0 ? ( + <> + + {errors} + + ) : null}
+ - +
); }; diff --git a/app/lib/theme/ReadOnlyFieldTemplate.tsx b/app/lib/theme/ReadOnlyFieldTemplate.tsx new file mode 100644 index 0000000000..1265faabbc --- /dev/null +++ b/app/lib/theme/ReadOnlyFieldTemplate.tsx @@ -0,0 +1,67 @@ +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faExclamationTriangle } from "@fortawesome/free-solid-svg-icons"; +import { FieldTemplateProps } from "@rjsf/core"; +import FieldLabel from "./widgets/FieldLabel"; + +const FieldTemplate: React.FC = ({ + children, + errors, + rawErrors, + label, + displayLabel, + required, + id, +}) => { + return ( +
+
+ {displayLabel && ( + + )} + {children} +
+ {rawErrors && rawErrors.length > 0 ? ( +
+ + {errors} +
+ ) : null} + + +
+ ); +}; + +export default FieldTemplate; diff --git a/app/lib/theme/ReadOnlyTheme.tsx b/app/lib/theme/ReadOnlyTheme.tsx index a84a1d28e5..dcfcaefd4f 100644 --- a/app/lib/theme/ReadOnlyTheme.tsx +++ b/app/lib/theme/ReadOnlyTheme.tsx @@ -1,5 +1,5 @@ import { ThemeProps } from "@rjsf/core"; -import FieldTemplate from "./FieldTemplate"; +import ReadOnlyFieldTemplate from "./ReadOnlyFieldTemplate"; import ReadOnlyObjectFieldTemplate from "./ReadOnlyObjectFieldTemplate"; import { utils } from "@rjsf/core"; import ReadOnlyWidget from "./widgets/ReadOnlyWidget"; @@ -23,7 +23,7 @@ const readOnlyTheme: ThemeProps = { SelectProjectStatusWidget: ReadOnlyWidget, }, ObjectFieldTemplate: ReadOnlyObjectFieldTemplate, - FieldTemplate: FieldTemplate, + FieldTemplate: ReadOnlyFieldTemplate, }; export default readOnlyTheme; diff --git a/app/lib/theme/utils/getRequiredLabel.ts b/app/lib/theme/utils/getRequiredLabel.ts deleted file mode 100644 index ae00ef1511..0000000000 --- a/app/lib/theme/utils/getRequiredLabel.ts +++ /dev/null @@ -1,4 +0,0 @@ -const getRequiredLabel = (label: string, required: boolean) => - label + (required ? "" : " (optional)"); - -export default getRequiredLabel; diff --git a/app/lib/theme/widgets/DisplayOnlyWidget.tsx b/app/lib/theme/widgets/DisplayOnlyWidget.tsx index c44a843c1e..f6a461a64b 100644 --- a/app/lib/theme/widgets/DisplayOnlyWidget.tsx +++ b/app/lib/theme/widgets/DisplayOnlyWidget.tsx @@ -1,12 +1,7 @@ import { WidgetProps } from "@rjsf/core"; -const DisplayOnlyWidget: React.FC = ({ options }) => { - return ( - - -

{options.text}

-
- ); +const DisplayOnlyWidget: React.FC = ({ options, id }) => { + return {options.text}; }; export default DisplayOnlyWidget; diff --git a/app/lib/theme/widgets/FieldLabel.tsx b/app/lib/theme/widgets/FieldLabel.tsx index 6aba3a2608..deeb0c826e 100644 --- a/app/lib/theme/widgets/FieldLabel.tsx +++ b/app/lib/theme/widgets/FieldLabel.tsx @@ -1,32 +1,33 @@ -import { UiSchema } from "@rjsf/core"; -import getRequiredLabel from "../utils/getRequiredLabel"; - interface Props { label: string; required: boolean; htmlFor: string; - uiSchema?: UiSchema; + tagName?: "label" | "dt"; } const FieldLabel: React.FC = ({ label, required, htmlFor, - uiSchema, + tagName = "label", }) => { - if ( - uiSchema && - uiSchema["ui:options"] && - uiSchema["ui:options"].label === false - ) { + if (!label) { return null; } + + const displayedLabel = label + (required ? "" : " (optional)") + " "; + + if (tagName === "label") + return ; + return ( <> - - {uiSchema && uiSchema["bcgov:help-text"] && ( -  {uiSchema["bcgov:help-text"]} - )} +
{displayedLabel}
+ ); }; diff --git a/app/lib/theme/widgets/MoneyWidget.tsx b/app/lib/theme/widgets/MoneyWidget.tsx index 4f4463275d..2bb1257492 100644 --- a/app/lib/theme/widgets/MoneyWidget.tsx +++ b/app/lib/theme/widgets/MoneyWidget.tsx @@ -1,25 +1,16 @@ import { WidgetProps } from "@rjsf/core"; import NumberFormat from "react-number-format"; -import FieldLabel from "./FieldLabel"; export const MoneyWidget: React.FC = ({ schema, id, disabled, label, - required, onChange, value, - uiSchema, }) => { return (
- = ({ schema, id, disabled, label, - required, onChange, value, - uiSchema, }) => { return ( <> - = ({ id, label, value }) => { +const ReadOnlyMoneyWidget: React.FC = ({ id, value }) => { return ( - <> -
{label}
-
- {value ? ( - - ) : ( - Not added - )} -
- +
+ {value ? ( + + ) : ( + Not added + )} +
); }; diff --git a/app/lib/theme/widgets/ReadOnlyWidget.tsx b/app/lib/theme/widgets/ReadOnlyWidget.tsx index e6ca612363..2698ab4bb3 100644 --- a/app/lib/theme/widgets/ReadOnlyWidget.tsx +++ b/app/lib/theme/widgets/ReadOnlyWidget.tsx @@ -1,11 +1,14 @@ import { WidgetProps } from "@rjsf/core"; -const ReadOnlyWidget: React.FC = ({ label, value, options }) => { +const ReadOnlyWidget: React.FC = ({ value, options }) => { return ( <> - {/* Some of the uiSchemas that use this widget have the label text in the label prop; others have it in options.title. Similarly, some uiSchemas have the value in the value prop; others in options.text. */} -
{options.title ?? label}
-
{options.text ?? value ?? Not added}
+ ); }; diff --git a/app/lib/theme/widgets/SearchDropdownWidget.tsx b/app/lib/theme/widgets/SearchDropdownWidget.tsx index 5812bf385b..5b21affa15 100644 --- a/app/lib/theme/widgets/SearchDropdownWidget.tsx +++ b/app/lib/theme/widgets/SearchDropdownWidget.tsx @@ -3,20 +3,10 @@ import { WidgetProps } from "@rjsf/core"; import Widgets from "@rjsf/core/dist/cjs/components/widgets"; import TextField from "@mui/material/TextField"; import Autocomplete from "@mui/material/Autocomplete"; -import FieldLabel from "./FieldLabel"; import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; const SearchDropdownWidget: React.FC = (props) => { - const { - id, - onChange, - schema, - placeholder, - readonly, - label, - required, - uiSchema, - } = props; + const { id, onChange, schema, placeholder, readonly } = props; const handleChange = (e: React.ChangeEvent<{}>, option: any) => { onChange(option?.value); @@ -33,52 +23,44 @@ const SearchDropdownWidget: React.FC = (props) => { if (readonly) return ; return ( - <> - - - props.value ? option.value === props.value : true - } - getOptionLabel={(option) => (option ? option.title : "")} - sx={{ - border: "2px solid #606060", - borderRadius: "0.25em", - marginTop: "0.2em", - "&.Mui-focused": { - outlineStyle: "solid", - outlineWidth: "4px", - outlineColor: "#3B99FC", - outlineOffset: "1px", - }, - }} - popupIcon={} - renderInput={(params) => { - return ( - - ); - }} - /> - + + props.value ? option.value === props.value : true + } + getOptionLabel={(option) => (option ? option.title : "")} + sx={{ + border: "2px solid #606060", + borderRadius: "0.25em", + marginTop: "0.2em", + "&.Mui-focused": { + outlineStyle: "solid", + outlineWidth: "4px", + outlineColor: "#3B99FC", + outlineOffset: "1px", + }, + }} + popupIcon={} + renderInput={(params) => { + return ( + + ); + }} + /> ); }; diff --git a/app/lib/theme/widgets/SelectWidget.tsx b/app/lib/theme/widgets/SelectWidget.tsx index a1f8006585..4d465eadb9 100644 --- a/app/lib/theme/widgets/SelectWidget.tsx +++ b/app/lib/theme/widgets/SelectWidget.tsx @@ -1,6 +1,5 @@ import { WidgetProps } from "@rjsf/core"; import Dropdown from "@button-inc/bcgov-theme/Dropdown"; -import FieldLabel from "./FieldLabel"; interface Option { type: string; @@ -28,12 +27,6 @@ const SelectWidget: React.FunctionComponent = (props) => { return (
- onChange(e.target.value || undefined)} diff --git a/app/lib/theme/widgets/TextAreaWidget.tsx b/app/lib/theme/widgets/TextAreaWidget.tsx index 86fd46837b..b6607f4130 100644 --- a/app/lib/theme/widgets/TextAreaWidget.tsx +++ b/app/lib/theme/widgets/TextAreaWidget.tsx @@ -1,6 +1,5 @@ import { WidgetProps } from "@rjsf/core"; import Textarea from "@button-inc/bcgov-theme/Textarea"; -import FieldLabel from "./FieldLabel"; const TextAreaWidget: React.FC = ({ id, @@ -9,16 +8,9 @@ const TextAreaWidget: React.FC = ({ label, value, required, - uiSchema, }) => { return ( - <> - +