Просмотр исходного кода

refactor field ids, inital work for required fields

Ivan Galakhov 4 лет назад
Родитель
Сommit
bb9d9bd0fa

+ 5 - 6
dashboard/src/components/form-refactor/PorterForm.tsx

@@ -26,7 +26,7 @@ const PorterForm: React.FC<Props> = (props) => {
     formData.tabs.length > 0 ? formData.tabs[0].name : ""
     formData.tabs.length > 0 ? formData.tabs[0].name : ""
   );
   );
 
 
-  const renderSectionField = (field: FormField, id: string): JSX.Element => {
+  const renderSectionField = (field: FormField): JSX.Element => {
     const bundledProps = {
     const bundledProps = {
       ...field,
       ...field,
       isReadOnly,
       isReadOnly,
@@ -37,9 +37,9 @@ const PorterForm: React.FC<Props> = (props) => {
       case "subtitle":
       case "subtitle":
         return <Helper>{field.label}</Helper>;
         return <Helper>{field.label}</Helper>;
       case "string-input":
       case "string-input":
-        return <StringInput id={id} {...(bundledProps as StringInputField)} />;
+        return <StringInput {...(bundledProps as StringInputField)} />;
       case "checkbox":
       case "checkbox":
-        return <Checkbox id={id} {...(bundledProps as CheckboxField)} />;
+        return <Checkbox {...(bundledProps as CheckboxField)} />;
     }
     }
     return <p>Not Implemented: {(field as any).type}</p>;
     return <p>Not Implemented: {(field as any).type}</p>;
   };
   };
@@ -48,10 +48,9 @@ const PorterForm: React.FC<Props> = (props) => {
     return (
     return (
       <>
       <>
         {section.contents.map((field, i) => {
         {section.contents.map((field, i) => {
-          const id = `${section.name}-${field.type}-${i}`;
           return (
           return (
-            <React.Fragment key={id}>
-              {renderSectionField(field, id)}
+            <React.Fragment key={field.id}>
+              {renderSectionField(field)}
             </React.Fragment>
             </React.Fragment>
           );
           );
         })}
         })}

+ 44 - 4
dashboard/src/components/form-refactor/PorterFormContextProvider.tsx

@@ -4,6 +4,7 @@ import {
   PorterFormState,
   PorterFormState,
   PorterFormAction,
   PorterFormAction,
   PorterFormVariableList,
   PorterFormVariableList,
+  GenericInputField,
 } from "./types";
 } from "./types";
 import { ShowIf, ShowIfAnd, ShowIfNot, ShowIfOr } from "../../shared/types";
 import { ShowIf, ShowIfAnd, ShowIfNot, ShowIfOr } from "../../shared/types";
 
 
@@ -121,6 +122,7 @@ export const PorterFormContextProvider: React.FC<Props> = (props) => {
   /*
   /*
   We don't want to have the actual <PorterForm> component to do as little form
   We don't want to have the actual <PorterForm> component to do as little form
   logic as possible, so this structures the form object based on show_if statements
   logic as possible, so this structures the form object based on show_if statements
+  and assigns a unique id to each field
 
 
   This computed structure also later lets us figure out which fields should be required
   This computed structure also later lets us figure out which fields should be required
   */
   */
@@ -130,18 +132,56 @@ export const PorterFormContextProvider: React.FC<Props> = (props) => {
   ) => {
   ) => {
     return {
     return {
       ...data,
       ...data,
-      tabs: data.tabs.map((tab) => {
+      tabs: data.tabs.map((tab, i) => {
         return {
         return {
           ...tab,
           ...tab,
-          sections: tab.sections.filter((section) => {
-            return !section.show_if || evalShowIf(section.show_if, variables);
-          }),
+          sections: tab.sections
+            .map((section, j) => {
+              return {
+                ...section,
+                contents: section.contents.map((field, k) => {
+                  return {
+                    ...field,
+                    id: `${i}-${j}-${k}`,
+                  };
+                }),
+              };
+            })
+            .filter((section) => {
+              return !section.show_if || evalShowIf(section.show_if, variables);
+            }),
         };
         };
       }),
       }),
     };
     };
   };
   };
+  /*
+    compute a list of field ids who's input is required and a map from a variable value
+    to a list of fields that set it
+  */
+  const computeRequiredVariables = (
+    data: PorterFormData
+  ): [string[], Record<string, string[]>] => {
+    const requiredIds: string[] = [];
+    const mapping: Record<string, string[]> = {};
+    data.tabs.map((tab) =>
+      tab.sections.map((section) =>
+        section.contents.map((field) => {
+          if (field.type == "heading" || field.type == "subtitle") return;
+          if (field.required) {
+            requiredIds.push(field.id);
+            if (!mapping[field.variable]) {
+              mapping[field.variable] = [];
+            }
+            mapping[field.variable].push(field.id);
+          }
+        })
+      )
+    );
+    return [requiredIds, mapping];
+  };
 
 
   const formData = computeFormStructure(props.rawFormData, state.variables);
   const formData = computeFormStructure(props.rawFormData, state.variables);
+  const [requiredIds, varMapping] = computeRequiredVariables(formData);
 
 
   return (
   return (
     <Provider
     <Provider

+ 1 - 1
dashboard/src/components/form-refactor/field-components/StringInput.tsx

@@ -2,7 +2,7 @@ import React from "react";
 import InputRow from "../../values-form/InputRow";
 import InputRow from "../../values-form/InputRow";
 import useFormField from "../hooks/useFormField";
 import useFormField from "../hooks/useFormField";
 import {
 import {
-  GenericFieldProps,
+  GenericInputField,
   StringInputField,
   StringInputField,
   StringInputFieldState,
   StringInputFieldState,
 } from "../types";
 } from "../types";

+ 10 - 7
dashboard/src/components/form-refactor/types.ts

@@ -5,17 +5,22 @@
 
 
 // YAML Field interfaces
 // YAML Field interfaces
 
 
-export interface GenericFieldProps {
+export interface GenericField {
+  id: string;
+}
+
+export interface GenericInputField extends GenericField {
   isReadOnly?: boolean;
   isReadOnly?: boolean;
   required?: boolean;
   required?: boolean;
+  variable: string;
 }
 }
 
 
-export interface HeadingField {
+export interface HeadingField extends GenericField{
   type: "heading";
   type: "heading";
   label: string;
   label: string;
 }
 }
 
 
-export interface SubtitleField {
+export interface SubtitleField extends GenericField{
   type: "subtitle";
   type: "subtitle";
   label: string;
   label: string;
 }
 }
@@ -26,19 +31,17 @@ export interface StringInputFieldSettings {
   omitUnitFromValue?: boolean;
   omitUnitFromValue?: boolean;
 }
 }
 
 
-export interface StringInputField extends GenericFieldProps {
+export interface StringInputField extends GenericInputField {
   type: "string-input";
   type: "string-input";
-  variable: string;
   label?: string;
   label?: string;
   placeholder?: string;
   placeholder?: string;
   info?: string;
   info?: string;
   settings?: StringInputFieldSettings;
   settings?: StringInputFieldSettings;
 }
 }
 
 
-export interface CheckboxField extends GenericFieldProps {
+export interface CheckboxField extends GenericInputField {
   type: "checkbox";
   type: "checkbox";
   label?: string;
   label?: string;
-  variable: string;
 }
 }
 
 
 export type FormField = HeadingField|SubtitleField|StringInputField|CheckboxField;
 export type FormField = HeadingField|SubtitleField|StringInputField|CheckboxField;