From e5d777c58d9c13dd46fc92ce64593795a779d95c Mon Sep 17 00:00:00 2001 From: Daniel Naab Date: Mon, 26 Feb 2024 11:40:57 -0600 Subject: [PATCH] Form edit initial wysiwyg (#51) * Add clickable preview component to the form manager's edit page. Remaining work is to style it, so it's not possible to click on form elements, tab to them, or enter data. * Preview UI has selectable patterns, to open the corresponding form element edit ui. * In-progress work wiring up saving to the WSYIWYG ui --- .../src/components/AppFormManager.tsx | 9 +- .../src/components/AppFormRouter.tsx | 12 +- packages/design/src/Form/Form.stories.tsx | 4 +- .../Form/PromptSegment/FormSummary/index.tsx | 14 --- .../design/src/Form/PromptSegment/index.tsx | 23 ---- packages/design/src/Form/index.tsx | 99 ++++++---------- .../FormManager/DocumentImporter/index.tsx | 17 ++- .../src/FormManager/FormEdit/ElementEdit.tsx | 0 .../FormManager/FormEdit/FormEdit.stories.tsx | 4 +- .../FormManager/FormEdit/FormElementEdit.tsx | 52 ++++++++ .../src/FormManager/FormEdit/Preview.tsx | 79 +++++++++++++ .../design/src/FormManager/FormEdit/index.tsx | 111 +++++++++++------- .../FormPreview/FormPreview.stories.tsx | 21 ++-- .../src/FormManager/FormPreview/index.tsx | 70 ++++------- packages/design/src/FormManager/index.tsx | 8 +- packages/design/src/FormRouter/index.tsx | 10 +- .../config/{ => edit}/InputElementEdit.tsx | 6 +- .../config/{ => edit}/SequenceElementEdit.tsx | 8 +- packages/design/src/config/edit/index.ts | 32 +++++ packages/design/src/config/index.ts | 27 +---- .../view}/FormSummary/FormSummary.stories.tsx | 2 +- .../view}/FormSummary/FormSummary.test.ts | 0 .../src/config/view/FormSummary/index.tsx | 16 +++ .../SubmissionConfirmation.stories.tsx | 2 +- .../view}/SubmissionConfirmation/index.tsx | 17 ++- .../view}/TextInput/TestInput.stories.tsx | 13 +- .../view}/TextInput/TestInput.test.ts | 0 .../view}/TextInput/index.tsx | 23 ++-- .../view}/additional-fields.tsx | 0 packages/design/src/config/view/index.tsx | 12 ++ packages/design/src/test-form.ts | 17 ++- packages/documents/src/document.ts | 2 +- packages/forms/src/config/elements/input.ts | 11 +- .../forms/src/config/elements/sequence.ts | 4 +- packages/forms/src/config/index.ts | 10 +- packages/forms/src/element.ts | 11 ++ packages/forms/src/index.ts | 33 ++++-- packages/forms/src/{prompt.ts => pattern.ts} | 60 ++++++---- packages/forms/src/response.ts | 3 +- packages/forms/src/session.ts | 29 +++++ packages/forms/tests/two-field-form.test.ts | 4 +- 41 files changed, 550 insertions(+), 325 deletions(-) delete mode 100644 packages/design/src/Form/PromptSegment/FormSummary/index.tsx delete mode 100644 packages/design/src/Form/PromptSegment/index.tsx create mode 100644 packages/design/src/FormManager/FormEdit/ElementEdit.tsx create mode 100644 packages/design/src/FormManager/FormEdit/FormElementEdit.tsx create mode 100644 packages/design/src/FormManager/FormEdit/Preview.tsx rename packages/design/src/config/{ => edit}/InputElementEdit.tsx (93%) rename packages/design/src/config/{ => edit}/SequenceElementEdit.tsx (93%) create mode 100644 packages/design/src/config/edit/index.ts rename packages/design/src/{Form/PromptSegment => config/view}/FormSummary/FormSummary.stories.tsx (97%) rename packages/design/src/{Form/PromptSegment => config/view}/FormSummary/FormSummary.test.ts (100%) create mode 100644 packages/design/src/config/view/FormSummary/index.tsx rename packages/design/src/{Form/PromptSegment => config/view}/SubmissionConfirmation/SubmissionConfirmation.stories.tsx (94%) rename packages/design/src/{Form/PromptSegment => config/view}/SubmissionConfirmation/index.tsx (66%) rename packages/design/src/{Form/PromptSegment => config/view}/TextInput/TestInput.stories.tsx (79%) rename packages/design/src/{Form/PromptSegment => config/view}/TextInput/TestInput.test.ts (100%) rename packages/design/src/{Form/PromptSegment => config/view}/TextInput/index.tsx (62%) rename packages/design/src/{Form/PromptSegment => config/view}/additional-fields.tsx (100%) create mode 100644 packages/design/src/config/view/index.tsx rename packages/forms/src/{prompt.ts => pattern.ts} (63%) diff --git a/apps/spotlight/src/components/AppFormManager.tsx b/apps/spotlight/src/components/AppFormManager.tsx index ad669db0..610526f6 100644 --- a/apps/spotlight/src/components/AppFormManager.tsx +++ b/apps/spotlight/src/components/AppFormManager.tsx @@ -1,6 +1,10 @@ import React from 'react'; -import { FormManager, defaultFormElementComponent } from '@atj/design'; +import { + FormManager, + defaultFormElementComponents, + defaultFormElementEditComponents, +} from '@atj/design'; import { getAppContext } from '../context'; @@ -10,7 +14,8 @@ export default function () { ; + return ( + + ); } diff --git a/packages/design/src/Form/Form.stories.tsx b/packages/design/src/Form/Form.stories.tsx index c99383db..1be46163 100644 --- a/packages/design/src/Form/Form.stories.tsx +++ b/packages/design/src/Form/Form.stories.tsx @@ -2,14 +2,14 @@ import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; import Form from '.'; -import { createTestFormConfig, createTestSession } from '../test-form'; +import { createTestFormContext, createTestSession } from '../test-form'; export default { title: 'Form', component: Form, decorators: [(Story, args) => ], args: { - config: createTestFormConfig(), + context: createTestFormContext(), session: createTestSession(), }, tags: ['autodocs'], diff --git a/packages/design/src/Form/PromptSegment/FormSummary/index.tsx b/packages/design/src/Form/PromptSegment/FormSummary/index.tsx deleted file mode 100644 index 4e5f6b2e..00000000 --- a/packages/design/src/Form/PromptSegment/FormSummary/index.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; - -import { FormSummaryPrompt } from '@atj/forms'; - -export type TextSummaryProps = { prompt: FormSummaryPrompt }; - -export default function FormSummary({ prompt }: TextSummaryProps) { - return ( - <> - {prompt.title} -

{prompt.description}

- - ); -} diff --git a/packages/design/src/Form/PromptSegment/index.tsx b/packages/design/src/Form/PromptSegment/index.tsx deleted file mode 100644 index 237785b3..00000000 --- a/packages/design/src/Form/PromptSegment/index.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; - -import { type PromptPart } from '@atj/forms'; -import FormSummary from './FormSummary'; -import SubmissionConfirmation from './SubmissionConfirmation'; -import TextInput from './TextInput'; - -export default function PromptSegment({ - promptPart, -}: { - promptPart: PromptPart; -}) { - if (promptPart.type === 'form-summary') { - return ; - } else if (promptPart.type === 'text') { - return ; - } else if (promptPart.type === 'submission-confirmation') { - return ; - } else { - const _exhaustiveCheck: never = promptPart; // eslint-disable-line @typescript-eslint/no-unused-vars - return (<>) as never; - } -} diff --git a/packages/design/src/Form/index.tsx b/packages/design/src/Form/index.tsx index 334cf531..c44d590c 100644 --- a/packages/design/src/Form/index.tsx +++ b/packages/design/src/Form/index.tsx @@ -8,11 +8,26 @@ import { type FormConfig, type FormSession, type Prompt, + type Pattern, } from '@atj/forms'; -import PromptSegment from './PromptSegment'; import ActionBar from './ActionBar'; +export type FormUIContext = { + config: FormConfig; + components: ComponentForPattern; +}; + +export type ComponentForPattern> = Record< + string, + FormElementComponent +>; + +export type FormElementComponent> = + React.ComponentType<{ + prompt: T; + }>; + const usePrompt = ( initialPrompt: Prompt, config: FormConfig, @@ -45,16 +60,22 @@ const usePrompt = ( }; export default function Form({ - config, + context, session, onSubmit, }: { - config: FormConfig; + context: FormUIContext; session: FormSession; onSubmit?: (data: Record) => void; }) { - const initialPrompt = createPrompt(config, session, { validate: false }); - const { prompt, updatePrompt } = usePrompt(initialPrompt, config, session); + const initialPrompt = createPrompt(context.config, session, { + validate: false, + }); + const { prompt, updatePrompt } = usePrompt( + initialPrompt, + context.config, + session + ); const formMethods = useForm>({}); @@ -65,10 +86,10 @@ export default function Form({ updatePrompt(allFormData); }, [allFormData]); */ - return ( -
{ updatePrompt(data); if (onSubmit) { @@ -80,9 +101,16 @@ export default function Form({ })} >
- {prompt.parts.map((promptPart, index) => ( - - ))} + {prompt.parts + .map((pattern, index) => { + if (pattern.type === 'text') { + console.log('skipping', pattern.type); + return null; + } + const Component = context.components[pattern.type]; + return ; + }) + .filter(a => a)} {/* Add submit button or other controls as needed */}
@@ -90,54 +118,3 @@ export default function Form({ ); } - -/* -export const FormFieldsetUnwired = ({ fields }: { fields: Field[] }) => { - return ( -
- - UD 105 - Unlawful Detainer Form - - {fields.map(field => { - // Use 'tag' for 'select' and 'textarea', 'type' for others - const fieldType = - field.tag === 'select' || - field.tag === 'textarea' || - field.tag === 'p' || - field.tag === 'h2' || - field.tag === 'h3' || - field.tag === 'ul' - ? field.tag - : field.type; - - switch (fieldType) { - case 'text': - return ; - case 'boolean': - return ; - case 'checkbox': - return ; - case 'select': - return ; - case 'radio': - return ; - case 'date': - return ; - case 'textarea': - return ; - case 'p': - return ; - case 'h2': - return ; - case 'h3': - return ; - case 'ul': - return ; - default: - return null; - } - })} -
- ); -}; -*/ diff --git a/packages/design/src/FormManager/DocumentImporter/index.tsx b/packages/design/src/FormManager/DocumentImporter/index.tsx index a2eb319c..ed61a5d0 100644 --- a/packages/design/src/FormManager/DocumentImporter/index.tsx +++ b/packages/design/src/FormManager/DocumentImporter/index.tsx @@ -4,24 +4,25 @@ import { useNavigate } from 'react-router-dom'; import { addDocument, addDocumentFieldsToForm } from '@atj/documents'; import { type FormService } from '@atj/form-service'; import { - type FormConfig, type DocumentFieldMap, type FormDefinition, + createFormSession, } from '@atj/forms'; import { onFileInputChangeGetFile } from '../FormList/PDFFileSelect/file-input'; import Form from '../../Form'; +import { FormUIContext } from 'config'; const DocumentImporter = ({ baseUrl, formId, - config, + context, form, formService, }: { baseUrl: string; formId: string; - config: FormConfig; + context: FormUIContext; form: FormDefinition; formService: FormService; }) => { @@ -70,7 +71,11 @@ const DocumentImporter = ({ const PDFFileSelect = () => { return (
-