From 6fc1d89d0bbd36d60f40cefc9a2947cfdc350af9 Mon Sep 17 00:00:00 2001 From: Daniel Naab Date: Mon, 15 Apr 2024 16:25:15 -0500 Subject: [PATCH] Wire drag and drop back up (#107) * Cleanup * In-progress * UI rendering with dnd controls, test passing; handles inactive. * dnd wired up to blueprint state. Added a test, which unfortunately does not work with JSDOM, so it is commented out * Remove unused import * Removed dist folder, which is in .gitignore --- packages/design/sass/styles.scss | 6 +- .../design/src/AvailableFormList/index.tsx | 18 +- packages/design/src/Form/index.tsx | 22 ++- .../src/FormManager/FormDelete/index.tsx | 4 +- .../FormManager/FormEdit/DraggableList.tsx | 77 +++----- .../FormManager/FormEdit/FormEdit.stories.tsx | 47 ++++- .../src/FormManager/FormEdit/Preview.tsx | 123 ++----------- .../FormManager/FormEdit/PreviewPattern.tsx | 49 ++++++ .../FormEdit/PreviewSequencePattern.tsx | 47 +++++ .../design/src/FormManager/FormEdit/store.tsx | 21 ++- .../FormManager/FormList/CreateNew/hooks.ts | 8 +- .../src/config/edit/SequencePatternEdit.tsx | 1 - packages/design/src/config/edit/index.ts | 4 +- .../design/src/config/view/Fieldset/index.tsx | 4 +- packages/forms/dist/index.js | 165 ------------------ packages/forms/src/builder/builder.test.ts | 6 +- packages/forms/src/builder/index.ts | 20 +-- packages/forms/src/components.ts | 24 +-- packages/forms/src/documents/document.ts | 14 +- .../forms/src/documents/pdf/parsing-api.ts | 5 +- packages/forms/src/patterns/address/index.ts | 2 +- packages/forms/src/patterns/fieldset.ts | 2 +- packages/forms/src/patterns/form-summary.ts | 2 +- packages/forms/src/patterns/input.ts | 2 +- packages/forms/src/patterns/paragraph.ts | 2 +- packages/forms/src/patterns/sequence.ts | 2 +- 26 files changed, 277 insertions(+), 400 deletions(-) create mode 100644 packages/design/src/FormManager/FormEdit/PreviewPattern.tsx create mode 100644 packages/design/src/FormManager/FormEdit/PreviewSequencePattern.tsx delete mode 100644 packages/forms/dist/index.js diff --git a/packages/design/sass/styles.scss b/packages/design/sass/styles.scss index 2b767991e..8ca6b0919 100644 --- a/packages/design/sass/styles.scss +++ b/packages/design/sass/styles.scss @@ -464,11 +464,11 @@ iframe:focus { .grid-col-8 { width: 100%; } - + .usa-button{ margin-left: 0; } - + .grid-col-4 { position: static; } @@ -476,7 +476,7 @@ iframe:focus { .form-group-row { padding: 0 0 1rem; } - + .settingsContainer { position: fixed; top: 10%; diff --git a/packages/design/src/AvailableFormList/index.tsx b/packages/design/src/AvailableFormList/index.tsx index 7606c7c31..fe78c62c1 100644 --- a/packages/design/src/AvailableFormList/index.tsx +++ b/packages/design/src/AvailableFormList/index.tsx @@ -34,20 +34,27 @@ const FormList = ({ urlForForm: UrlForForm; }) => { return ( - - - - + + + {forms.map((form, index) => ( - +
Available forms
Form titleDescriptionActions + Form title + + Description + + Actions +
{form.title} + {form.title} + {form.description} @@ -58,6 +65,5 @@ const FormList = ({ ))}
- ); }; diff --git a/packages/design/src/Form/index.tsx b/packages/design/src/Form/index.tsx index 4c2f978aa..ee60b9e33 100644 --- a/packages/design/src/Form/index.tsx +++ b/packages/design/src/Form/index.tsx @@ -9,7 +9,7 @@ import { type FormSession, type PatternProps, type Prompt, - type PromptPart, + type PromptComponent, } from '@atj/forms'; import ActionBar from './ActionBar'; @@ -232,12 +232,12 @@ export default function Form({ )}
- {prompt.parts.map((part, index) => { + {prompt.components.map((component, index) => { return ( ); })} @@ -253,17 +253,21 @@ export default function Form({ const PromptComponent = ({ context, - promptPart, + component, }: { context: FormUIContext; - promptPart: PromptPart; + component: PromptComponent; }) => { - const Component = context.components[promptPart.pattern.type]; + const Component = context.components[component.props.type]; return ( - - {promptPart.children?.map((child, index) => { + + {component.children?.map((childPromptComponent, index) => { return ( - + ); })} diff --git a/packages/design/src/FormManager/FormDelete/index.tsx b/packages/design/src/FormManager/FormDelete/index.tsx index 4fdda5a7d..83d7eb603 100644 --- a/packages/design/src/FormManager/FormDelete/index.tsx +++ b/packages/design/src/FormManager/FormDelete/index.tsx @@ -25,7 +25,9 @@ export default function FormDelete({

Delete form

Are you sure you want to delete the form with id: `{formId}`?

- +

{JSON.stringify(form, null, 4)} diff --git a/packages/design/src/FormManager/FormEdit/DraggableList.tsx b/packages/design/src/FormManager/FormEdit/DraggableList.tsx index 99bc32d22..8aa15c638 100644 --- a/packages/design/src/FormManager/FormEdit/DraggableList.tsx +++ b/packages/design/src/FormManager/FormEdit/DraggableList.tsx @@ -1,4 +1,4 @@ -import React, { Children, useState } from 'react'; +import React, { Children } from 'react'; import { DndContext, closestCenter, @@ -6,6 +6,7 @@ import { PointerSensor, useSensor, useSensors, + UniqueIdentifier, } from '@dnd-kit/core'; import { arrayMove, @@ -16,32 +17,24 @@ import { } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; -import { - getPattern, - type Blueprint, - type Pattern, - PatternId, -} from '@atj/forms'; - -import { SequencePattern } from '@atj/forms/src/patterns/sequence'; - const SortableItem = ({ id, children, }: { - id: string; + id: UniqueIdentifier; children: React.ReactNode; }) => { const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id }); - const style = { - transform: CSS.Transform.toString(transform), - transition, - }; - return ( -
  • +
    {children}
    -
  • + ); }; type DraggableListProps = React.PropsWithChildren<{ - pattern: Pattern; - form: Blueprint; - setSelectedPattern: (pattern: Pattern) => void; + order: UniqueIdentifier[]; + updateOrder: (order: UniqueIdentifier[]) => void; }>; export const DraggableList: React.FC = ({ - pattern, - form, - setSelectedPattern, children, + order, + updateOrder, }) => { - const [patterns, setPatterns] = useState( - pattern.data.patterns.map((patternId: PatternId) => { - return getPattern(form, patternId); - }) - ); const sensors = useSensors( useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) ); const arrayChildren = Children.toArray(children); + return ( = ({ return; } if (active.id !== over.id) { - const oldIndex = patterns.findIndex(pattern => { - return pattern.id === active.id; - }); - const newIndex = patterns.findIndex(pattern => { - return pattern.id === over.id; - }); - const newOrder = arrayMove(patterns, oldIndex, newIndex); - setPatterns(newOrder); - setSelectedPattern({ - id: pattern.id, - type: pattern.type, - data: { - patterns: newOrder.map(pattern => pattern.id), - }, - } satisfies SequencePattern); + const oldIndex = order.indexOf(active.id); + const newIndex = order.indexOf(over.id); + const newOrder = arrayMove(order, oldIndex, newIndex); + updateOrder(newOrder); } }} > - -
      - {arrayChildren.map((child, index) => ( - + + {arrayChildren.map((child, index) => { + const patternId = order[index]; + return ( + {child} - ))} -
    + ); + })}
    ); diff --git a/packages/design/src/FormManager/FormEdit/FormEdit.stories.tsx b/packages/design/src/FormManager/FormEdit/FormEdit.stories.tsx index ce8571978..1e25dbb16 100644 --- a/packages/design/src/FormManager/FormEdit/FormEdit.stories.tsx +++ b/packages/design/src/FormManager/FormEdit/FormEdit.stories.tsx @@ -30,8 +30,8 @@ export default { export const FormEditTest: StoryObj = { play: async ({ canvasElement }) => { - await editFieldLabel(canvasElement, 0, 'First field label'); - await editFieldLabel(canvasElement, 1, 'Second field label'); + await editFieldLabel(canvasElement, 1, 'First field label'); + await editFieldLabel(canvasElement, 2, 'Second field label'); }, }; @@ -40,20 +40,53 @@ export const FormEditAddPattern: StoryObj = { const canvas = within(canvasElement); // Select the first pattern for editing - const button = (await canvas.findAllByRole('button'))[0]; + const button = (await canvas.findAllByLabelText('Edit form group'))[0]; await userEvent.click(button); // Get the initial count of inputs - const initialCount = (await canvas.getAllByRole('textbox')).length; + const initialCount = (await canvas.findAllByLabelText('Edit form group')) + .length; const select = canvas.getByLabelText('Add a pattern'); await userEvent.selectOptions(select, 'Text input'); - const finalCount = (await canvas.getAllByRole('textbox')).length; + const finalCount = (await canvas.findAllByLabelText('Edit form group')) + .length; expect(finalCount).toEqual(initialCount + 1); }, }; +// This test only works in a real browser, not via JSDOM as we use it. +/* +export const FormEditReorderPattern: StoryObj = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + const grabber = await canvas.getAllByText(':::')[0]; + await grabber.focus(); + + // Enter reordering mode with the spacebar + await userEvent.type(grabber, ' '); + await new Promise(r => setTimeout(r, 100)); + + // Press the arrow down, to move the first pattern to the second position + await userEvent.type(grabber, '[ArrowDown]'); + await new Promise(r => setTimeout(r, 100)); + + // Press the spacebar to exit reordering mode + await userEvent.type(grabber, ' '); + await new Promise(r => setTimeout(r, 100)); + + // Pattern 1 should be after pattern 2 in the document + const pattern1 = canvas.getByText('Pattern 1'); + const pattern2 = canvas.getByText('Pattern 2'); + expect(pattern2.compareDocumentPosition(pattern1)).toBe( + Node.DOCUMENT_POSITION_FOLLOWING + ); + }, +}; +*/ + const editFieldLabel = async ( element: HTMLElement, buttonIndex: number, @@ -62,7 +95,9 @@ const editFieldLabel = async ( const canvas = within(element); // Click "edit form" button for first field - await userEvent.click(canvas.getAllByRole('button')[buttonIndex]); + await userEvent.click( + (await canvas.findAllByLabelText('Edit form group'))[buttonIndex] + ); // Enter new text for first field const input = canvas.getByLabelText('Field label'); diff --git a/packages/design/src/FormManager/FormEdit/Preview.tsx b/packages/design/src/FormManager/FormEdit/Preview.tsx index 2482d52e0..a04ddaff6 100644 --- a/packages/design/src/FormManager/FormEdit/Preview.tsx +++ b/packages/design/src/FormManager/FormEdit/Preview.tsx @@ -2,135 +2,46 @@ import React from 'react'; import { createFormSession } from '@atj/forms'; -import Form, { - type ComponentForPattern, - type PatternComponent, - type FormUIContext, -} from '../../Form'; +import Form, { PatternComponent, type ComponentForPattern } from '../../Form'; + +import { PreviewPattern } from './PreviewPattern'; +import { PatternPreviewSequence } from './PreviewSequencePattern'; import { useFormEditStore } from './store'; export const PreviewForm = () => { const uiContext = useFormEditStore(state => state.context); const form = useFormEditStore(state => state.form); - const previewUiContext: FormUIContext = { - config: uiContext.config, - // TODO: We'll want to hoist this definition up to a higher level, so we - // don't have to regenerate it every time we render the form. - components: createPreviewComponents( - uiContext.components, - uiContext.uswdsRoot - ), - uswdsRoot: uiContext.uswdsRoot, - }; const disposable = createFormSession(form); // nullSession instead? return (
    ); }; const createPreviewComponents = ( - components: ComponentForPattern, - uswdsRoot: string + components: ComponentForPattern ): ComponentForPattern => { const previewComponents: ComponentForPattern = {}; // TODO: Create a configurable way to to define preview components. for (const [patternType, Component] of Object.entries(components)) { - if (patternType === 'sequence' || patternType === 'fieldset') { - previewComponents[patternType] = Component; - /* - previewComponents[patternType] = createSequencePatternPreviewComponent( - Component, - previewComponents - ); - */ - } else if (patternType === 'form-summary') { - previewComponents[patternType] = Component; + previewComponents[patternType] = Component; + if (patternType === 'sequence') { + previewComponents[patternType] = + PatternPreviewSequence as PatternComponent; } else { - //previewComponents[patternType] = Component; - previewComponents[patternType] = createPatternPreviewComponent( - Component, - uswdsRoot - ); + previewComponents[patternType] = PreviewPattern; } } return previewComponents; }; - -/* -const createSequencePatternPreviewComponent = ( - Component: PatternComponent, - previewComponents: ComponentForPattern -) => { - const PatternPreviewSequenceComponent: PatternComponent = ({ - pattern, - }) => { - const { form, setSelectedPattern } = usePreviewContext(); - const element = getPattern(form, props._patternId); - const Component = previewComponents[props.type]; - return ( - - - - ); - }; - return PatternPreviewSequenceComponent; -}; -*/ - -const createPatternPreviewComponent = ( - Component: PatternComponent, - uswdsRoot: string -) => { - const PatternPreviewComponent: PatternComponent = props => { - const context = useFormEditStore(state => state.context); - const selectedPattern = useFormEditStore(state => state.selectedPattern); - const handleEditClick = useFormEditStore(state => state.handleEditClick); - - const isSelected = selectedPattern?.id === props._patternId; - const divClassNames = isSelected - ? 'form-group-row field-selected' - : 'form-group-row'; - const EditComponent = context.editComponents[props.type]; - - return ( -
    - - - {EditComponent ? ( - - ) : null} - -
    - ); - }; - return PatternPreviewComponent; -}; diff --git a/packages/design/src/FormManager/FormEdit/PreviewPattern.tsx b/packages/design/src/FormManager/FormEdit/PreviewPattern.tsx new file mode 100644 index 000000000..262727262 --- /dev/null +++ b/packages/design/src/FormManager/FormEdit/PreviewPattern.tsx @@ -0,0 +1,49 @@ +import React from 'react'; + +import { PatternComponent } from '../../Form'; +import { useFormEditStore } from './store'; + +export const PreviewPattern: PatternComponent = function PreviewPattern(props) { + const context = useFormEditStore(state => state.context); + const selectedPattern = useFormEditStore(state => state.selectedPattern); + const handleEditClick = useFormEditStore(state => state.handleEditClick); + + const isSelected = selectedPattern?.id === props._patternId; + const divClassNames = isSelected + ? 'form-group-row field-selected' + : 'form-group-row'; + const Component = context.components[props.type]; + const EditComponent = context.editComponents[props.type]; + + return ( +
    + + + {EditComponent ? ( + + ) : null} + +
    + ); +}; diff --git a/packages/design/src/FormManager/FormEdit/PreviewSequencePattern.tsx b/packages/design/src/FormManager/FormEdit/PreviewSequencePattern.tsx new file mode 100644 index 000000000..800957bdd --- /dev/null +++ b/packages/design/src/FormManager/FormEdit/PreviewSequencePattern.tsx @@ -0,0 +1,47 @@ +import React from 'react'; + +import { PatternProps, getPattern } from '@atj/forms'; + +import { PatternComponent } from '../../Form'; +import DraggableList from './DraggableList'; +import { useFormEditStore } from './store'; +import { SequencePattern } from '@atj/forms/src/patterns/sequence'; + +// TODO: consider merging this component with DraggableList, to clean up +// sematics around how its children are handled. +export const PatternPreviewSequence: PatternComponent< + PatternProps +> = function PatternPreviewSequence(props) { + const form = useFormEditStore(state => state.form); + const updatePattern = useFormEditStore(state => state.updatePattern); + const pattern = getPattern(form, props._patternId); + + /** + * Here, we assume that we are rendering a "sequence" pattern, and that + * sequences have no styling of their own. If sequences were to get their + * own styling (like other components), this component would need to be + * updated to replicate the styles, or the wrapping structure would need to + * be updated to ensure that we pass the correct children to DraggableList. + * + * In other words, we'd want to render: + * const Component = context.components[props.type]; + * ... and then something like: + * {props.children} + */ + return ( + { + updatePattern({ + id: pattern.id, + type: pattern.type, + data: { + patterns: order, + }, + }); + }} + > + {props.children} + + ); +}; diff --git a/packages/design/src/FormManager/FormEdit/store.tsx b/packages/design/src/FormManager/FormEdit/store.tsx index 3522e1ba3..02d4ed653 100644 --- a/packages/design/src/FormManager/FormEdit/store.tsx +++ b/packages/design/src/FormManager/FormEdit/store.tsx @@ -8,7 +8,7 @@ import { type PatternId, type PatternMap, getPattern, - FormBuilder, + BlueprintBuilder, } from '@atj/forms'; import { type FormEditUIContext } from './types'; @@ -40,6 +40,7 @@ type FormEditState = { addPattern: (patternType: string) => void; handleEditClick: (patternId: PatternId) => void; setSelectedPattern: (element?: Pattern) => void; + updatePattern: (data: Pattern) => void; updateSelectedPattern: (formData: PatternMap) => void; }; @@ -61,7 +62,7 @@ const createFormEditStore = ({ ), addPattern: (patternType: string) => { const state = get(); - const builder = new FormBuilder(state.form); + const builder = new BlueprintBuilder(state.form); const newPattern = builder.addPattern(state.context.config, patternType); set({ form: builder.form, selectedPattern: newPattern }); }, @@ -75,13 +76,27 @@ const createFormEditStore = ({ } }, setSelectedPattern: selectedPattern => set({ selectedPattern }), + updatePattern: (pattern: Pattern) => { + const state = get(); + const builder = new BlueprintBuilder(state.form); + const success = builder.updatePattern( + state.context.config, + state.form.patterns[pattern.id], + { + [pattern.id]: pattern, + } + ); + if (success) { + set({ form: builder.form, selectedPattern: undefined }); + } + }, updateSelectedPattern: (formData: PatternMap) => { const state = get(); if (state.selectedPattern === undefined) { console.warn('No selected element'); return; } - const builder = new FormBuilder(state.form); + const builder = new BlueprintBuilder(state.form); const success = builder.updatePattern( state.context.config, state.selectedPattern, diff --git a/packages/design/src/FormManager/FormList/CreateNew/hooks.ts b/packages/design/src/FormManager/FormList/CreateNew/hooks.ts index 1b744876b..f42a568fd 100644 --- a/packages/design/src/FormManager/FormList/CreateNew/hooks.ts +++ b/packages/design/src/FormManager/FormList/CreateNew/hooks.ts @@ -1,6 +1,6 @@ import { useNavigate } from 'react-router-dom'; -import { FormBuilder } from '@atj/forms'; +import { BlueprintBuilder } from '@atj/forms'; import { type FormService } from '@atj/form-service'; export const useDocumentImporter = ( @@ -14,7 +14,7 @@ export const useDocumentImporter = ( stepOneSelectPdfByUrl: async (url: string) => { const data = await fetchUint8Array(`${baseUrl}${url}`); - const builder = new FormBuilder(); + const builder = new BlueprintBuilder(); builder.setFormSummary({ title: url, description: '', @@ -33,7 +33,7 @@ export const useDocumentImporter = ( name: string; data: Uint8Array; }) => { - const builder = new FormBuilder(); + const builder = new BlueprintBuilder(); builder.setFormSummary({ title: fileDetails.name, description: '', @@ -45,7 +45,7 @@ export const useDocumentImporter = ( } }, createNewForm: async () => { - const builder = new FormBuilder(); + const builder = new BlueprintBuilder(); builder.setFormSummary({ title: `My form - ${new Date().toISOString()}`, description: '', diff --git a/packages/design/src/config/edit/SequencePatternEdit.tsx b/packages/design/src/config/edit/SequencePatternEdit.tsx index 836d7c720..11cf6ccfa 100644 --- a/packages/design/src/config/edit/SequencePatternEdit.tsx +++ b/packages/design/src/config/edit/SequencePatternEdit.tsx @@ -82,7 +82,6 @@ const SequencePatternEdit: PatternEditComponent = ({ useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) ); - return (
    = props => { +const Fieldset: PatternComponent = props => { return (
    {props.legend} @@ -12,4 +12,4 @@ const FormSummary: PatternComponent = props => {
    ); }; -export default FormSummary; +export default Fieldset; diff --git a/packages/forms/dist/index.js b/packages/forms/dist/index.js deleted file mode 100644 index ab83e7d48..000000000 --- a/packages/forms/dist/index.js +++ /dev/null @@ -1,165 +0,0 @@ -"use strict"; -var __defProp = Object.defineProperty; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __hasOwnProp = Object.prototype.hasOwnProperty; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { get: all[name], enumerable: true }); -}; -var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; -}; -var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); - -// src/index.ts -var src_exports = {}; -__export(src_exports, { - addFormOutput: () => addFormOutput, - addQuestions: () => addQuestions, - createForm: () => createForm, - createFormContext: () => createFormContext, - createPrompt: () => createPrompt, - getFlatFieldList: () => getFlatFieldList, - updateForm: () => updateForm -}); -module.exports = __toCommonJS(src_exports); - -// src/prompts/index.ts -var createPrompt = (formContext) => { - const parts = [ - { - type: "form-summary", - title: formContext.form.summary.title, - description: formContext.form.summary.description - } - ]; - if (formContext.form.strategy.type === "sequential") { - parts.push( - ...formContext.form.strategy.order.map((questionId) => { - const question = formContext.form.questions[questionId]; - return { - type: "text", - id: question.id, - value: formContext.context.values[questionId], - label: question.text, - required: question.required - }; - }) - ); - } else if (formContext.form.strategy.type === "null") { - } else { - const _exhaustiveCheck = formContext.form.strategy; - } - return parts; -}; - -// src/index.ts -var createForm = (summary, questions = []) => { - return { - summary, - questions: getQuestionMap(questions), - strategy: { - type: "sequential", - order: questions.map((question) => { - return question.id; - }) - }, - documents: [] - }; -}; -var createFormContext = (form) => { - return { - context: { - errors: {}, - values: Object.fromEntries( - Object.values(form.questions).map((question) => { - return [question.id, question.initial]; - }) - ) - }, - form - }; -}; -var updateForm = (context, id, value) => { - if (!(id in context.form.questions)) { - console.error(`Question "${id}" does not exist on form.`); - return context; - } - const nextForm = addValue(context, id, value); - if (context.form.questions[id].required && !value) { - return addError(nextForm, id, "Required value not provided."); - } - return nextForm; -}; -var addValue = (form, id, value) => ({ - ...form, - context: { - ...form.context, - values: { - ...form.context.values, - [id]: value - } - } -}); -var addError = (form, id, error) => ({ - ...form, - context: { - ...form.context, - errors: { - ...form.context.errors, - [id]: error - } - } -}); -var getQuestionMap = (questions) => { - return Object.fromEntries( - questions.map((question) => { - return [question.id, question]; - }) - ); -}; -var addQuestions = (form, questions) => { - const questionMap = getQuestionMap(questions); - return { - ...form, - questions: { ...form.questions, ...questionMap }, - strategy: { - ...form.strategy, - order: [...form.strategy.order, ...Object.keys(questionMap)] - } - }; -}; -var getFlatFieldList = (form) => { - if (form.strategy.type === "sequential") { - return form.strategy.order.map((questionId) => { - return form.questions[questionId]; - }); - } else if (form.strategy.type === "null") { - return []; - } else { - const _exhaustiveCheck = form.strategy; - return _exhaustiveCheck; - } -}; -var addFormOutput = (form, document) => { - return { - ...form, - documents: [...form.documents, document] - }; -}; -// Annotate the CommonJS export names for ESM import in node: -0 && (module.exports = { - addFormOutput, - addQuestions, - createForm, - createFormContext, - createPrompt, - getFlatFieldList, - updateForm -}); diff --git a/packages/forms/src/builder/builder.test.ts b/packages/forms/src/builder/builder.test.ts index 51fd144b4..2efb176ec 100644 --- a/packages/forms/src/builder/builder.test.ts +++ b/packages/forms/src/builder/builder.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; -import { FormBuilder } from '.'; +import { BlueprintBuilder } from '.'; import { createForm } from '..'; import { defaultFormConfig } from '../patterns'; import { type InputPattern } from '../patterns/input'; @@ -8,7 +8,7 @@ import { type SequencePattern } from '../patterns/sequence'; describe('form builder', () => { it('addPattern adds initial pattern of given type', () => { - const builder = new FormBuilder(); + const builder = new BlueprintBuilder(); expect(Object.keys(builder.form.patterns).length).toEqual(1); builder.addPattern(defaultFormConfig, 'input'); expect(Object.keys(builder.form.patterns).length).toEqual(2); @@ -16,7 +16,7 @@ describe('form builder', () => { it('addPattern preserves existing structure', () => { const initial = createTestBlueprint(); - const newBuilder = new FormBuilder(initial); + const newBuilder = new BlueprintBuilder(initial); const newPattern = newBuilder.addPattern(defaultFormConfig, 'input'); expect(newBuilder.form.patterns[newPattern.id]).toEqual(newPattern); expect( diff --git a/packages/forms/src/builder/index.ts b/packages/forms/src/builder/index.ts index 2c82ae7a9..adacdf65f 100644 --- a/packages/forms/src/builder/index.ts +++ b/packages/forms/src/builder/index.ts @@ -12,43 +12,43 @@ import { addPatternToRoot, } from '..'; -export class FormBuilder { - private _form: Blueprint; +export class BlueprintBuilder { + private _bp: Blueprint; constructor(initial: Blueprint = nullBlueprint) { - this._form = initial; + this._bp = initial; } get form(): Blueprint { - return this._form; + return this._bp; } setFormSummary(summary: FormSummary) { - this._form = updateFormSummary(this.form, summary); + this._bp = updateFormSummary(this.form, summary); } async addDocument(fileDetails: { name: string; data: Uint8Array }) { const { updatedForm } = await addDocument(this.form, fileDetails); - this._form = updatedForm; + this._bp = updatedForm; } addPattern(config: FormConfig, patternType: string) { const pattern = createPattern(config, patternType); - this._form = addPatternToRoot(this.form, pattern); + this._bp = addPatternToRoot(this.form, pattern); return pattern; } updatePattern(config: FormConfig, pattern: Pattern, formData: PatternMap) { - const updatedElement = updatePatternFromFormData( + const updatedBlueprint = updatePatternFromFormData( config, this.form, pattern, formData ); - if (!updatedElement) { + if (!updatedBlueprint) { return false; } - this._form = updatedElement; + this._bp = updatedBlueprint; return true; } } diff --git a/packages/forms/src/components.ts b/packages/forms/src/components.ts index c3bdfb3c1..af080c964 100644 --- a/packages/forms/src/components.ts +++ b/packages/forms/src/components.ts @@ -55,14 +55,14 @@ export type SubmitAction = { }; export type PromptAction = SubmitAction; -export type PromptPart = { - pattern: PatternProps; - children: PromptPart[]; +export type PromptComponent = { + props: PatternProps; + children: PromptComponent[]; }; export type Prompt = { actions: PromptAction[]; - parts: PromptPart[]; + components: PromptComponent[]; }; export const createPrompt = ( @@ -73,9 +73,9 @@ export const createPrompt = ( if (options.validate && sessionIsComplete(config, session)) { return { actions: [], - parts: [ + components: [ { - pattern: { + props: { _patternId: 'submission-confirmation', type: 'submission-confirmation', table: Object.entries(session.data.values) @@ -98,9 +98,9 @@ export const createPrompt = ( ], }; } - const parts: PromptPart[] = [ + const components: PromptComponent[] = [ { - pattern: { + props: { _patternId: 'form-summary', type: 'form-summary', title: session.form.summary.title, @@ -110,7 +110,7 @@ export const createPrompt = ( }, ]; const root = getRootPattern(session.form); - parts.push(createPromptForPattern(config, session, root, options)); + components.push(createPromptForPattern(config, session, root, options)); return { actions: [ { @@ -118,7 +118,7 @@ export const createPrompt = ( text: 'Submit', }, ], - parts, + components, }; }; @@ -127,7 +127,7 @@ export type CreatePrompt = ( session: FormSession, pattern: T, options: { validate: boolean } -) => PromptPart; +) => PromptComponent; export const createPromptForPattern: CreatePrompt = ( config, @@ -152,7 +152,7 @@ export const createNullPrompt = ({ }): Prompt => { const formPatternConfig = getPatternConfig(config, pattern.type); return { - parts: [ + components: [ formPatternConfig.createPrompt(config, nullSession, pattern, { validate: false, }), diff --git a/packages/forms/src/documents/document.ts b/packages/forms/src/documents/document.ts index f619725d7..8a2d85005 100644 --- a/packages/forms/src/documents/document.ts +++ b/packages/forms/src/documents/document.ts @@ -22,21 +22,21 @@ export const addDocument = async ( } ) => { const fields = await getDocumentFieldData(fileDetails.data); - const cachedPdf = await getSuggestedPatterns(fileDetails.data); + const parsedPdf = await getSuggestedPatterns(fileDetails.data); - if (cachedPdf) { + if (parsedPdf) { form = updateFormSummary(form, { - title: cachedPdf.title, + title: parsedPdf.title, description: '', }); - form = addPatternMap(form, cachedPdf.patterns, cachedPdf.root); + form = addPatternMap(form, parsedPdf.patterns, parsedPdf.root); const updatedForm = addFormOutput(form, { data: fileDetails.data, path: fileDetails.name, - fields: cachedPdf.outputs, + fields: parsedPdf.outputs, formFields: Object.fromEntries( - Object.keys(cachedPdf.outputs).map(output => { - return [output, cachedPdf.outputs[output].name]; + Object.keys(parsedPdf.outputs).map(output => { + return [output, parsedPdf.outputs[output].name]; }) ), }); diff --git a/packages/forms/src/documents/pdf/parsing-api.ts b/packages/forms/src/documents/pdf/parsing-api.ts index 3f8de9e1a..922d77f56 100644 --- a/packages/forms/src/documents/pdf/parsing-api.ts +++ b/packages/forms/src/documents/pdf/parsing-api.ts @@ -70,7 +70,7 @@ const RawBtnField = z.object({ var_name: z.string(), field_dict: z.object({ font_info: z.string(), - flags: z.number(), + flags: z.unknown().optional(), field_type: z.string(), field_label: z.string(), child_fields: z.array(z.object({ coordinates: z.number().array() })), @@ -116,9 +116,6 @@ export const callExternalParser = async ( } const json = await response.json(); - - console.log('json.parsed_pdf.elements is:\n', json.parsed_pdf.elements); - const extracted: ExtractedObject = ExtractedObject.parse(json.parsed_pdf); const parsedPdf: ParsedPdf = { diff --git a/packages/forms/src/patterns/address/index.ts b/packages/forms/src/patterns/address/index.ts index 14cb71f33..28520afd7 100644 --- a/packages/forms/src/patterns/address/index.ts +++ b/packages/forms/src/patterns/address/index.ts @@ -98,7 +98,7 @@ export const addressConfig: PatternConfig< ? AddressSchema.safeParse(sessionValue) : null; return { - pattern: { + props: { _patternId: pattern.id, type: 'address', childProps: { diff --git a/packages/forms/src/patterns/fieldset.ts b/packages/forms/src/patterns/fieldset.ts index a6b3cfdde..58e89179b 100644 --- a/packages/forms/src/patterns/fieldset.ts +++ b/packages/forms/src/patterns/fieldset.ts @@ -36,7 +36,7 @@ export const fieldsetConfig: PatternConfig = { return createPromptForPattern(config, session, childPattern, options); }); return { - pattern: { + props: { _patternId: pattern.id, type: 'fieldset', legend: pattern.data.legend, diff --git a/packages/forms/src/patterns/form-summary.ts b/packages/forms/src/patterns/form-summary.ts index d17fe6224..4da29978b 100644 --- a/packages/forms/src/patterns/form-summary.ts +++ b/packages/forms/src/patterns/form-summary.ts @@ -22,7 +22,7 @@ export const formSummaryConfig: PatternConfig = { }, createPrompt(_, session, pattern, options) { return { - pattern: { + props: { _patternId: pattern.id, type: 'form-summary', title: pattern.data.title, diff --git a/packages/forms/src/patterns/input.ts b/packages/forms/src/patterns/input.ts index 329c2414b..1a1ca98be 100644 --- a/packages/forms/src/patterns/input.ts +++ b/packages/forms/src/patterns/input.ts @@ -43,7 +43,7 @@ export const inputConfig: PatternConfig = { } } return { - pattern: { + props: { _patternId: pattern.id, type: 'input', inputId: pattern.id, diff --git a/packages/forms/src/patterns/paragraph.ts b/packages/forms/src/patterns/paragraph.ts index 54701db0c..9dd020e26 100644 --- a/packages/forms/src/patterns/paragraph.ts +++ b/packages/forms/src/patterns/paragraph.ts @@ -22,7 +22,7 @@ export const paragraphConfig: PatternConfig = { }, createPrompt(_, session, pattern, options) { return { - pattern: { + props: { _patternId: pattern.id, type: 'paragraph' as const, text: pattern.data.text, diff --git a/packages/forms/src/patterns/sequence.ts b/packages/forms/src/patterns/sequence.ts index e92456342..fff149f08 100644 --- a/packages/forms/src/patterns/sequence.ts +++ b/packages/forms/src/patterns/sequence.ts @@ -34,7 +34,7 @@ export const sequenceConfig: PatternConfig = { return createPromptForPattern(config, session, childPattern, options); }); return { - pattern: { + props: { _patternId: pattern.id, type: 'sequence', },