From 2a6086d273b95cf5927646fdbc42aea1c48dd89a Mon Sep 17 00:00:00 2001 From: Daniel Naab Date: Fri, 29 Mar 2024 09:09:48 -0500 Subject: [PATCH 1/6] Add appropriate type annotations to each literal pattern. --- packages/design/src/test-form.ts | 6 +++--- packages/forms/src/documents/document.ts | 7 +++++-- packages/forms/src/documents/pdf/mock-api.ts | 3 ++- packages/forms/src/index.ts | 2 +- packages/forms/tests/two-field-form.test.ts | 22 +++++++++++++++----- 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/packages/design/src/test-form.ts b/packages/design/src/test-form.ts index 4a77f193..1b9418bb 100644 --- a/packages/design/src/test-form.ts +++ b/packages/design/src/test-form.ts @@ -27,7 +27,7 @@ export const createTestForm = () => { initial: { patterns: [], }, - } as SequencePattern, + } satisfies SequencePattern, { type: 'input', id: 'element-1', @@ -43,7 +43,7 @@ export const createTestForm = () => { required: true, maxLength: 128, }, - } as InputPattern, + } satisfies InputPattern, { type: 'input', id: 'element-2', @@ -59,7 +59,7 @@ export const createTestForm = () => { required: true, maxLength: 128, }, - } as InputPattern, + } satisfies InputPattern, ], } ); diff --git a/packages/forms/src/documents/document.ts b/packages/forms/src/documents/document.ts index 53b9a6a4..262f7d62 100644 --- a/packages/forms/src/documents/document.ts +++ b/packages/forms/src/documents/document.ts @@ -7,6 +7,7 @@ import { updateFormSummary, } from '..'; import { InputPattern } from '../patterns/input'; +import { SequencePattern } from '../patterns/sequence'; import { PDFDocument, getDocumentFieldData } from './pdf'; import { getSuggestedPatternsFromCache } from './suggestions'; import { DocumentFieldMap } from './types'; @@ -154,7 +155,9 @@ export const addDocumentFieldsToForm = ( data: { patterns: patterns.map(pattern => pattern.id), }, - initial: [], - }); + initial: { + patterns: [], + }, + } satisfies SequencePattern); return addPatterns(form, patterns, 'root'); }; diff --git a/packages/forms/src/documents/pdf/mock-api.ts b/packages/forms/src/documents/pdf/mock-api.ts index 118d9f5f..464179ce 100644 --- a/packages/forms/src/documents/pdf/mock-api.ts +++ b/packages/forms/src/documents/pdf/mock-api.ts @@ -5,6 +5,7 @@ import { type Pattern, type PatternId, type PatternMap } from '../..'; import { type FieldsetPattern } from '../../patterns/fieldset'; import { type InputPattern } from '../../patterns/input'; import { type ParagraphPattern } from '../../patterns/paragraph'; +import { SequencePattern } from '../../patterns/sequence'; import { stringToBase64 } from '../util'; import { type DocumentFieldMap } from '../types'; @@ -174,7 +175,7 @@ export const parseAlabamaNameChangeForm = (): ParsedPdf => { initial: { patterns: [], }, - }; + } satisfies SequencePattern; return parsedPdf; }; diff --git a/packages/forms/src/index.ts b/packages/forms/src/index.ts index f42c39a2..af5a71a2 100644 --- a/packages/forms/src/index.ts +++ b/packages/forms/src/index.ts @@ -39,7 +39,7 @@ export const nullBlueprint: Blueprint = { initial: { patterns: [], }, - }, + } satisfies SequencePattern, }, outputs: [], }; diff --git a/packages/forms/tests/two-field-form.test.ts b/packages/forms/tests/two-field-form.test.ts index 42bc70b1..cbc34caf 100644 --- a/packages/forms/tests/two-field-form.test.ts +++ b/packages/forms/tests/two-field-form.test.ts @@ -1,6 +1,8 @@ import { describe, expect, test } from 'vitest'; import * as forms from '../src'; +import { SequencePattern } from '../src/patterns/sequence'; +import { InputPattern } from '../src/patterns/input'; const patterns: forms.Pattern[] = [ { @@ -12,7 +14,7 @@ const patterns: forms.Pattern[] = [ initial: { patterns: [], }, - }, + } satisfies SequencePattern, { type: 'input', id: 'pattern-1', @@ -21,8 +23,13 @@ const patterns: forms.Pattern[] = [ initial: '', required: true, }, - initial: '', - }, + initial: { + label: '', + initial: '', + required: true, + maxLength: 128, + }, + } satisfies InputPattern, { type: 'input', id: 'pattern-2', @@ -31,8 +38,13 @@ const patterns: forms.Pattern[] = [ initial: '', required: false, }, - initial: '', - }, + initial: { + label: '', + initial: '', + required: true, + maxLength: 128, + }, + } satisfies InputPattern, ]; const form = forms.createForm( { From 949c1faea592953cfcef5e4d444fd4b9c72005d8 Mon Sep 17 00:00:00 2001 From: Daniel Naab Date: Fri, 29 Mar 2024 10:00:04 -0500 Subject: [PATCH 2/6] Remove references to mismatched "initial" values; also, remove initial session values (this isn't necessary now, and is problematic. might need to revisit later) --- .../FormManager/FormEdit/DraggableList.tsx | 3 --- .../src/config/edit/InputPatternEdit.tsx | 6 ++--- packages/design/src/test-form.ts | 15 ----------- packages/forms/src/builder/builder.test.ts | 21 +++------------- packages/forms/src/documents/document.ts | 18 ------------- packages/forms/src/documents/pdf/mock-api.ts | 25 ++++++------------- packages/forms/src/index.ts | 6 ----- packages/forms/src/pattern.ts | 8 +++--- packages/forms/src/patterns/form-summary.ts | 6 ++--- packages/forms/src/session.ts | 12 +++++---- packages/forms/tests/two-field-form.test.ts | 17 ++----------- 11 files changed, 27 insertions(+), 110 deletions(-) diff --git a/packages/design/src/FormManager/FormEdit/DraggableList.tsx b/packages/design/src/FormManager/FormEdit/DraggableList.tsx index a33cbb71..99bc32d2 100644 --- a/packages/design/src/FormManager/FormEdit/DraggableList.tsx +++ b/packages/design/src/FormManager/FormEdit/DraggableList.tsx @@ -104,9 +104,6 @@ export const DraggableList: React.FC = ({ data: { patterns: newOrder.map(pattern => pattern.id), }, - initial: { - patterns: [], - }, } satisfies SequencePattern); } }} diff --git a/packages/design/src/config/edit/InputPatternEdit.tsx b/packages/design/src/config/edit/InputPatternEdit.tsx index ce208244..111f9abe 100644 --- a/packages/design/src/config/edit/InputPatternEdit.tsx +++ b/packages/design/src/config/edit/InputPatternEdit.tsx @@ -21,14 +21,14 @@ const InputPatternEdit: PatternEditComponent = ({ pattern }) => { >
-
diff --git a/packages/design/src/test-form.ts b/packages/design/src/test-form.ts index 1b9418bb..fd090d74 100644 --- a/packages/design/src/test-form.ts +++ b/packages/design/src/test-form.ts @@ -24,9 +24,6 @@ export const createTestForm = () => { data: { patterns: ['element-1', 'element-2'], }, - initial: { - patterns: [], - }, } satisfies SequencePattern, { type: 'input', @@ -37,12 +34,6 @@ export const createTestForm = () => { required: true, maxLength: 128, }, - initial: { - label: 'Pattern 1', - initial: '', - required: true, - maxLength: 128, - }, } satisfies InputPattern, { type: 'input', @@ -53,12 +44,6 @@ export const createTestForm = () => { required: true, maxLength: 128, }, - initial: { - label: 'Pattern 2', - initial: 'test', - required: true, - maxLength: 128, - }, } satisfies InputPattern, ], } diff --git a/packages/forms/src/builder/builder.test.ts b/packages/forms/src/builder/builder.test.ts index a99b32db..51fd144b 100644 --- a/packages/forms/src/builder/builder.test.ts +++ b/packages/forms/src/builder/builder.test.ts @@ -40,10 +40,7 @@ export const createTestBlueprint = () => { data: { patterns: ['element-1', 'element-2'], }, - initial: { - patterns: [], - }, - } as SequencePattern, + } satisfies SequencePattern, { type: 'input', id: 'element-1', @@ -53,13 +50,7 @@ export const createTestBlueprint = () => { required: true, maxLength: 128, }, - initial: { - label: 'Pattern 1', - initial: '', - required: true, - maxLength: 128, - }, - } as InputPattern, + } satisfies InputPattern, { type: 'input', id: 'element-2', @@ -69,13 +60,7 @@ export const createTestBlueprint = () => { required: true, maxLength: 128, }, - initial: { - label: 'Pattern 2', - initial: 'test', - required: true, - maxLength: 128, - }, - } as InputPattern, + } satisfies InputPattern, ], } ); diff --git a/packages/forms/src/documents/document.ts b/packages/forms/src/documents/document.ts index 262f7d62..bc15cf0a 100644 --- a/packages/forms/src/documents/document.ts +++ b/packages/forms/src/documents/document.ts @@ -77,9 +77,6 @@ export const addDocumentFieldsToForm = ( id: patternId, data: { label: field.label, - }, - initial: { - label: '', initial: '', required: false, maxLength: 128, @@ -91,9 +88,6 @@ export const addDocumentFieldsToForm = ( id: patternId, data: { label: field.label, - }, - initial: { - label: '', initial: '', required: false, maxLength: 128, @@ -105,9 +99,6 @@ export const addDocumentFieldsToForm = ( id: patternId, data: { label: field.label, - }, - initial: { - label: '', initial: '', required: false, maxLength: 128, @@ -119,9 +110,6 @@ export const addDocumentFieldsToForm = ( id: patternId, data: { label: field.label, - }, - initial: { - label: '', initial: '', required: false, maxLength: 128, @@ -133,9 +121,6 @@ export const addDocumentFieldsToForm = ( id: patternId, data: { label: field.label, - }, - initial: { - label: '', initial: '', required: false, maxLength: 128, @@ -155,9 +140,6 @@ export const addDocumentFieldsToForm = ( data: { patterns: patterns.map(pattern => pattern.id), }, - initial: { - patterns: [], - }, } satisfies SequencePattern); return addPatterns(form, patterns, 'root'); }; diff --git a/packages/forms/src/documents/pdf/mock-api.ts b/packages/forms/src/documents/pdf/mock-api.ts index 464179ce..5dedc714 100644 --- a/packages/forms/src/documents/pdf/mock-api.ts +++ b/packages/forms/src/documents/pdf/mock-api.ts @@ -112,13 +112,9 @@ export const parseAlabamaNameChangeForm = (): ParsedPdf => { parsedPdf.patterns[element.id] = { type: 'paragraph', id: element.id, - initial: { - text: '', - maxLength: 2048, - }, data: { text: element.element_params.text, - style: element.element_params.text_style, + maxLength: 2048, }, } satisfies ParagraphPattern; rootSequence.push(element.id); @@ -130,15 +126,12 @@ export const parseAlabamaNameChangeForm = (): ParsedPdf => { parsedPdf.patterns[id] = { type: 'input', id, - initial: { + data: { + label: input.input_params.instructions, required: false, - label: '', initial: '', maxLength: 128, }, - data: { - label: input.input_params.instructions, - }, } satisfies InputPattern; fieldsetPatterns.push(id); parsedPdf.outputs[id] = { @@ -159,10 +152,7 @@ export const parseAlabamaNameChangeForm = (): ParsedPdf => { legend: element.element_params.text, patterns: fieldsetPatterns, }, - initial: { - patterns: [], - }, - } as FieldsetPattern; + } satisfies FieldsetPattern; rootSequence.push(element.id); } } @@ -172,9 +162,6 @@ export const parseAlabamaNameChangeForm = (): ParsedPdf => { data: { patterns: rootSequence, }, - initial: { - patterns: [], - }, } satisfies SequencePattern; return parsedPdf; }; @@ -186,9 +173,11 @@ const getElementInputs = (element: ExtractedElement): Pattern[] => { return { type: 'input', id: input.input_params.output_id, - initial: {} as unknown as any, data: { label: input.input_params.instructions, + required: false, + maxLength: 256, + initial: '', }, } satisfies InputPattern; } diff --git a/packages/forms/src/index.ts b/packages/forms/src/index.ts index af5a71a2..e471939e 100644 --- a/packages/forms/src/index.ts +++ b/packages/forms/src/index.ts @@ -36,9 +36,6 @@ export const nullBlueprint: Blueprint = { data: { patterns: [], }, - initial: { - patterns: [], - }, } satisfies SequencePattern, }, outputs: [], @@ -69,9 +66,6 @@ export const createForm = ( data: { patterns: [], }, - initial: { - patterns: [], - }, } satisfies SequencePattern, ], root: 'root', diff --git a/packages/forms/src/pattern.ts b/packages/forms/src/pattern.ts index fb29aca5..e20f40e9 100644 --- a/packages/forms/src/pattern.ts +++ b/packages/forms/src/pattern.ts @@ -3,22 +3,21 @@ import { updatePattern, type Blueprint } from '..'; import { type CreatePrompt } from './components'; -export type Pattern = { +export type Pattern = { type: string; id: PatternId; data: C; - initial: T; }; export type PatternId = string; -export type PatternValue = T['initial']; +export type PatternValue = any; export type PatternValueMap = Record; export type PatternMap = Record; export type GetPattern = (form: Blueprint, id: PatternId) => Pattern; export type ParsePatternData = ( patternData: T['data'], - obj: string + obj: any ) => Result; export type ParsePatternConfigData = ( @@ -139,6 +138,5 @@ export const createPattern = ( id: generatePatternId(), type: patternType, data: config.patterns[patternType].initial, - initial: {}, }; }; diff --git a/packages/forms/src/patterns/form-summary.ts b/packages/forms/src/patterns/form-summary.ts index cb20aa0a..9ee9780a 100644 --- a/packages/forms/src/patterns/form-summary.ts +++ b/packages/forms/src/patterns/form-summary.ts @@ -14,10 +14,8 @@ export const formSummaryConfig: PatternConfig = { displayName: 'Form summary', acceptsInput: false, initial: { - text: '', - initial: '', - required: true, - maxLength: 128, + title: 'Form title', + summary: 'Form extended description', }, parseData: obj => safeZodParse(configSchema, obj), // make this optional? parseConfigData: obj => safeZodParse(configSchema, obj), diff --git a/packages/forms/src/session.ts b/packages/forms/src/session.ts index 4b854366..e26063de 100644 --- a/packages/forms/src/session.ts +++ b/packages/forms/src/session.ts @@ -34,12 +34,10 @@ export const nullSession: FormSession = { root: { id: 'root', type: 'sequence', - required: false, - initial: { + data: { patterns: [], }, - data: {}, - } as SequencePattern, + } satisfies SequencePattern, }, root: 'root', summary: { @@ -54,11 +52,15 @@ export const createFormSession = (form: Blueprint): FormSession => { return { data: { errors: {}, + values: {}, + /* values: Object.fromEntries( - Object.values(form.patterns).map((pattern, index) => { + Object.values(form.patterns).map(pattern => { + //return [pattern.id, config.patterns[pattern.id].initial]; return [pattern.id, form.patterns[pattern.id].data.initial]; }) ), + */ }, form, }; diff --git a/packages/forms/tests/two-field-form.test.ts b/packages/forms/tests/two-field-form.test.ts index cbc34caf..269b69cf 100644 --- a/packages/forms/tests/two-field-form.test.ts +++ b/packages/forms/tests/two-field-form.test.ts @@ -11,20 +11,12 @@ const patterns: forms.Pattern[] = [ data: { patterns: ['pattern-1', 'pattern-2'], }, - initial: { - patterns: [], - }, } satisfies SequencePattern, { type: 'input', id: 'pattern-1', data: { - text: 'What is your first name?', - initial: '', - required: true, - }, - initial: { - label: '', + label: 'What is your first name?', initial: '', required: true, maxLength: 128, @@ -34,12 +26,7 @@ const patterns: forms.Pattern[] = [ type: 'input', id: 'pattern-2', data: { - text: 'What is your favorite word?', - initial: '', - required: false, - }, - initial: { - label: '', + label: 'What is your favorite word?', initial: '', required: true, maxLength: 128, From 0795388d612f3e2cbb8bc8af3c0b52ad63024833 Mon Sep 17 00:00:00 2001 From: Daniel Naab Date: Fri, 29 Mar 2024 12:38:54 -0500 Subject: [PATCH 3/6] Separate pattern config data type from pattern output data. --- .../src/config/view/FormSummary/index.tsx | 2 +- .../src/config/view/Paragraph/index.tsx | 32 +++----------- packages/forms/src/components.ts | 4 +- packages/forms/src/pattern.ts | 43 +++++++++---------- packages/forms/src/patterns/fieldset.ts | 6 --- packages/forms/src/patterns/form-summary.ts | 6 +-- packages/forms/src/patterns/input.ts | 9 ++-- packages/forms/src/patterns/paragraph.ts | 8 +--- packages/forms/src/patterns/sequence.ts | 6 --- packages/forms/src/response.ts | 11 ----- packages/forms/src/util/zod.ts | 4 +- 11 files changed, 40 insertions(+), 91 deletions(-) diff --git a/packages/design/src/config/view/FormSummary/index.tsx b/packages/design/src/config/view/FormSummary/index.tsx index 0d77b53a..a2f01de7 100644 --- a/packages/design/src/config/view/FormSummary/index.tsx +++ b/packages/design/src/config/view/FormSummary/index.tsx @@ -9,7 +9,7 @@ const FormSummary: PatternComponent = ({ pattern }) => {
{/* {pattern.title} */}

{pattern.title}

- {pattern.description !== '' &&

{pattern.description}

} + {pattern.summary !== '' &&

{pattern.summary}

}
); diff --git a/packages/design/src/config/view/Paragraph/index.tsx b/packages/design/src/config/view/Paragraph/index.tsx index ef01808f..dacf4967 100644 --- a/packages/design/src/config/view/Paragraph/index.tsx +++ b/packages/design/src/config/view/Paragraph/index.tsx @@ -5,32 +5,10 @@ import { type ParagraphProps } from '@atj/forms'; import { type PatternComponent } from '../../../Form'; const FormSummary: PatternComponent = ({ pattern }) => { - if (pattern.style === 'heading') { - return ( - <> -

{pattern.text}

- - ); - } else if (pattern.style === 'subheading') { - return ( - <> -

{pattern.text}

- - ); - } else if (pattern.style === 'indent') { - return ( - <> -
    -
  • {pattern.text}
  • -
- - ); - } else { - return ( - <> -

{pattern.text}

- - ); - } + return ( + <> +

{pattern.text}

+ + ); }; export default FormSummary; diff --git a/packages/forms/src/components.ts b/packages/forms/src/components.ts index 51f79572..f67e6309 100644 --- a/packages/forms/src/components.ts +++ b/packages/forms/src/components.ts @@ -35,7 +35,7 @@ export type ParagraphProps = PatternProps<{ export type FieldsetProps = PatternProps<{ type: 'fieldset'; - legend: string; + legend?: string; }>; export type PatternProps = { @@ -79,7 +79,7 @@ export const createPrompt = ( config, session.form.patterns[patternId].type ); - return elemConfig.acceptsInput; + return !!elemConfig.parseData; }) .map(([patternId, value]) => { return { diff --git a/packages/forms/src/pattern.ts b/packages/forms/src/pattern.ts index e20f40e9..fedf8a26 100644 --- a/packages/forms/src/pattern.ts +++ b/packages/forms/src/pattern.ts @@ -15,38 +15,37 @@ export type PatternValueMap = Record; export type PatternMap = Record; export type GetPattern = (form: Blueprint, id: PatternId) => Pattern; -export type ParsePatternData = ( - patternData: T['data'], - obj: any -) => Result; +type ParsePatternData = ( + patternData: PatternConfigData, + obj: unknown +) => Result; -export type ParsePatternConfigData = ( - patternData: T['data'] -) => Result; +type ParsePatternConfigData = ( + patternData: unknown +) => Result; export const getPattern: GetPattern = (form, patternId) => { return form.patterns[patternId]; }; -export type PatternConfig = { +export type PatternConfig< + ThisPattern extends Pattern = Pattern, + PatternOutput = unknown, +> = { displayName: string; - acceptsInput: boolean; initial: ThisPattern['data']; - parseData: ParsePatternData; - parseConfigData: ParsePatternConfigData; + parseData?: ParsePatternData; + parseConfigData: ParsePatternConfigData; getChildren: ( pattern: ThisPattern, patterns: Record ) => Pattern[]; createPrompt: CreatePrompt; }; -export type FormConfig = { - patterns: Record>; -}; -export type ConfigPatterns = ReturnType< - Config['patterns'][keyof Config['patterns']]['parseData'] ->; +export type FormConfig = { + patterns: Record>; +}; export const getPatternMap = (patterns: Pattern[]) => { return Object.fromEntries( @@ -64,24 +63,24 @@ export const getPatternConfig = ( }; export const validatePattern = ( - elementConfig: PatternConfig, - element: Pattern, + patternConfig: PatternConfig, + pattern: Pattern, value: any ): Result => { - if (!elementConfig.acceptsInput) { + if (!patternConfig.parseData) { return { success: true, data: value, }; } - const parseResult = elementConfig.parseData(element, value); + const parseResult = patternConfig.parseData(pattern, value); if (!parseResult.success) { return { success: false, error: parseResult.error, }; } - if (element.data.required && !parseResult.data) { + if (pattern.data.required && !parseResult.data) { return { success: false, error: 'Required value not provided.', diff --git a/packages/forms/src/patterns/fieldset.ts b/packages/forms/src/patterns/fieldset.ts index 96812523..9d452144 100644 --- a/packages/forms/src/patterns/fieldset.ts +++ b/packages/forms/src/patterns/fieldset.ts @@ -14,8 +14,6 @@ export type FieldsetPattern = Pattern<{ patterns: PatternId[]; }>; -const FieldsetSchema = z.array(z.string()); - const configSchema = z.object({ legend: z.string().optional(), patterns: z.array(z.string()), @@ -23,13 +21,9 @@ const configSchema = z.object({ export const fieldsetConfig: PatternConfig = { displayName: 'Fieldset', - acceptsInput: false, initial: { patterns: [], }, - parseData: (_, obj) => { - return safeZodParse(FieldsetSchema, obj); - }, parseConfigData: obj => safeZodParse(configSchema, obj), getChildren(pattern, patterns) { return pattern.data.patterns.map( diff --git a/packages/forms/src/patterns/form-summary.ts b/packages/forms/src/patterns/form-summary.ts index 9ee9780a..d17fe622 100644 --- a/packages/forms/src/patterns/form-summary.ts +++ b/packages/forms/src/patterns/form-summary.ts @@ -6,18 +6,16 @@ import { safeZodParse } from '../util/zod'; const configSchema = z.object({ title: z.string().max(128), - summary: z.string().max(2024), + description: z.string().max(2024), }); export type FormSummary = Pattern>; export const formSummaryConfig: PatternConfig = { displayName: 'Form summary', - acceptsInput: false, initial: { title: 'Form title', - summary: 'Form extended description', + description: 'Form extended description', }, - parseData: obj => safeZodParse(configSchema, obj), // make this optional? parseConfigData: obj => safeZodParse(configSchema, obj), getChildren() { return []; diff --git a/packages/forms/src/patterns/input.ts b/packages/forms/src/patterns/input.ts index 54190d9a..329c2414 100644 --- a/packages/forms/src/patterns/input.ts +++ b/packages/forms/src/patterns/input.ts @@ -16,16 +16,19 @@ export type InputPattern = Pattern>; const createSchema = (data: InputPattern['data']) => z.string().max(data.maxLength); -export const inputConfig: PatternConfig = { +type InputPatternOutput = z.infer>; + +export const inputConfig: PatternConfig = { displayName: 'Text input', - acceptsInput: true, initial: { label: '', initial: '', required: true, maxLength: 128, }, - parseData: (patternData, obj) => safeZodParse(createSchema(patternData), obj), + parseData: (patternData, obj) => { + return safeZodParse(createSchema(patternData), obj); + }, parseConfigData: obj => safeZodParse(configSchema, obj), getChildren() { return []; diff --git a/packages/forms/src/patterns/paragraph.ts b/packages/forms/src/patterns/paragraph.ts index ac1e98c0..54701db0 100644 --- a/packages/forms/src/patterns/paragraph.ts +++ b/packages/forms/src/patterns/paragraph.ts @@ -10,17 +10,12 @@ const configSchema = z.object({ }); export type ParagraphPattern = Pattern>; -const createSchema = (data: ParagraphPattern['data']) => - z.string().max(data.maxLength); - export const paragraphConfig: PatternConfig = { displayName: 'Paragraph', - acceptsInput: false, initial: { - text: 'normal', + text: 'Paragraph text...', maxLength: 2048, }, - parseData: (patternData, obj) => safeZodParse(createSchema(patternData), obj), parseConfigData: obj => safeZodParse(configSchema, obj), getChildren() { return []; @@ -31,7 +26,6 @@ export const paragraphConfig: PatternConfig = { _patternId: pattern.id, type: 'paragraph' as const, text: pattern.data.text, - style: pattern.data.style, } as ParagraphProps, children: [], }; diff --git a/packages/forms/src/patterns/sequence.ts b/packages/forms/src/patterns/sequence.ts index 2701145a..b09d5ad1 100644 --- a/packages/forms/src/patterns/sequence.ts +++ b/packages/forms/src/patterns/sequence.ts @@ -13,21 +13,15 @@ export type SequencePattern = Pattern<{ patterns: PatternId[]; }>; -const sequenceSchema = z.array(z.string()); - const configSchema = z.object({ patterns: z.array(z.string()), }); export const sequenceConfig: PatternConfig = { displayName: 'Sequence', - acceptsInput: false, initial: { patterns: [], }, - parseData: (_, obj) => { - return safeZodParse(sequenceSchema, obj); - }, parseConfigData: obj => safeZodParse(configSchema, obj), getChildren(pattern, patterns) { return pattern.data.patterns.map( diff --git a/packages/forms/src/response.ts b/packages/forms/src/response.ts index 3e3db137..46df833d 100644 --- a/packages/forms/src/response.ts +++ b/packages/forms/src/response.ts @@ -37,17 +37,6 @@ export const applyPromptResponse = ( }; }; -const parsePatternValue = ( - config: FormConfig, - session: FormSession, - patternId: PatternId, - promptValue: string -) => { - const pattern = session.form.patterns[patternId]; - const patternConfig = getPatternConfig(config, pattern.type); - return patternConfig.parseData(pattern, promptValue); -}; - const parsePromptResponse = ( session: FormSession, config: FormConfig, diff --git a/packages/forms/src/util/zod.ts b/packages/forms/src/util/zod.ts index 262bfe5b..3a8060f3 100644 --- a/packages/forms/src/util/zod.ts +++ b/packages/forms/src/util/zod.ts @@ -6,8 +6,8 @@ import { type Pattern } from '..'; export const safeZodParse = ( schema: z.Schema, - obj: string -): Result => { + obj: unknown +): Result => { const result = schema.safeParse(obj); if (result.success) { return { From 6c007e0c5c0b771d331068ec6206ff0872301fa0 Mon Sep 17 00:00:00 2001 From: Daniel Naab Date: Fri, 29 Mar 2024 16:09:26 -0500 Subject: [PATCH 4/6] Added initial address component, using text inputs sequentially. Will also implement a unified address component, once this is fully working. TODO: handle address component validation in an orderly way and add to UI. --- packages/design/src/Form/index.tsx | 11 +- .../src/FormManager/FormEdit/Preview.tsx | 24 ++- .../design/src/FormManager/FormEdit/store.tsx | 12 +- .../design/src/config/view/Fieldset/index.tsx | 9 +- .../view/FormSummary/FormSummary.stories.tsx | 10 +- .../src/config/view/FormSummary/index.tsx | 6 +- .../src/config/view/Paragraph/index.tsx | 4 +- .../SubmissionConfirmation.stories.tsx | 16 +- .../view/SubmissionConfirmation/index.tsx | 8 +- .../view/TextInput/TestInput.stories.tsx | 28 ++-- .../src/config/view/TextInput/index.tsx | 32 ++-- .../src/patterns/address/address.test.ts | 0 packages/forms/src/patterns/address/index.ts | 84 +++++++++++ .../src/patterns/address/jurisdictions.ts | 139 ++++++++++++++++++ packages/forms/src/patterns/index.ts | 2 + 15 files changed, 298 insertions(+), 87 deletions(-) create mode 100644 packages/forms/src/patterns/address/address.test.ts create mode 100644 packages/forms/src/patterns/address/index.ts create mode 100644 packages/forms/src/patterns/address/jurisdictions.ts diff --git a/packages/design/src/Form/index.tsx b/packages/design/src/Form/index.tsx index 5295b4a1..4c2f978a 100644 --- a/packages/design/src/Form/index.tsx +++ b/packages/design/src/Form/index.tsx @@ -25,10 +25,11 @@ export type ComponentForPattern< > = Record>; export type PatternComponent> = - React.ComponentType<{ - pattern: T; - children?: React.ReactNode; - }>; + React.ComponentType< + T & { + children?: React.ReactNode; + } + >; const usePrompt = ( initialPrompt: Prompt, @@ -259,7 +260,7 @@ const PromptComponent = ({ }) => { const Component = context.components[promptPart.pattern.type]; return ( - + {promptPart.children?.map((child, index) => { return ( diff --git a/packages/design/src/FormManager/FormEdit/Preview.tsx b/packages/design/src/FormManager/FormEdit/Preview.tsx index 2777a1a9..1cec63c4 100644 --- a/packages/design/src/FormManager/FormEdit/Preview.tsx +++ b/packages/design/src/FormManager/FormEdit/Preview.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { type PatternProps, createFormSession } from '@atj/forms'; +import { createFormSession } from '@atj/forms'; import Form, { type ComponentForPattern, @@ -71,15 +71,15 @@ const createSequencePatternPreviewComponent = ( pattern, }) => { const { form, setSelectedPattern } = usePreviewContext(); - const element = getPattern(form, pattern._patternId); - const Component = previewComponents[pattern.type]; + const element = getPattern(form, props._patternId); + const Component = previewComponents[props.type]; return ( - + ); }; @@ -91,29 +91,25 @@ const createPatternPreviewComponent = ( Component: PatternComponent, uswdsRoot: string ) => { - const PatternPreviewComponent: PatternComponent = ({ - pattern, - }: { - pattern: PatternProps; - }) => { + const PatternPreviewComponent: PatternComponent = props => { const selectedPattern = useFormEditStore(state => state.selectedPattern); const handleEditClick = useFormEditStore(state => state.handleEditClick); - const isSelected = selectedPattern?.id === pattern._patternId; + const isSelected = selectedPattern?.id === props._patternId; const divClassNames = isSelected ? 'form-group-row field-selected' : 'form-group-row'; return ( -
- +
+
diff --git a/packages/design/src/FormManager/FormEdit/AddPattern.tsx b/packages/design/src/FormManager/FormEdit/AddPattern.tsx index 9b80fee0..b2d04e21 100644 --- a/packages/design/src/FormManager/FormEdit/AddPattern.tsx +++ b/packages/design/src/FormManager/FormEdit/AddPattern.tsx @@ -10,7 +10,7 @@ export const AddPattern = () => { return (
+ ); +}; +export default Address; diff --git a/packages/design/src/config/view/ZipCode/index.tsx b/packages/design/src/config/view/ZipCode/index.tsx new file mode 100644 index 00000000..9c1f8238 --- /dev/null +++ b/packages/design/src/config/view/ZipCode/index.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { useFormContext } from 'react-hook-form'; + +import { type ZipcodeProps } from '@atj/forms'; + +export const ZipCode = (props: ZipcodeProps) => { + const { register } = useFormContext(); + return ( + <> + + + + ); +}; diff --git a/packages/design/src/config/view/index.tsx b/packages/design/src/config/view/index.tsx index 0066eb4e..43aa1064 100644 --- a/packages/design/src/config/view/index.tsx +++ b/packages/design/src/config/view/index.tsx @@ -1,3 +1,4 @@ +import Address from './Address'; import Fieldset from './Fieldset'; import FormSummary from './FormSummary'; import Paragraph from './Paragraph'; @@ -7,6 +8,7 @@ import TextInput from './TextInput'; import { type ComponentForPattern } from '../../Form'; export const defaultPatternComponents: ComponentForPattern = { + address: Address, fieldset: Fieldset, 'form-summary': FormSummary, input: TextInput, diff --git a/packages/forms/src/components.ts b/packages/forms/src/components.ts index f67e6309..c3bdfb3c 100644 --- a/packages/forms/src/components.ts +++ b/packages/forms/src/components.ts @@ -38,9 +38,14 @@ export type FieldsetProps = PatternProps<{ legend?: string; }>; +export type ZipcodeProps = PatternProps<{ + type: 'zipcode'; + inputId: string; + value: string; +}>; + export type PatternProps = { _patternId: PatternId; - _children: PromptPart[]; type: string; } & T; diff --git a/packages/forms/src/patterns/address/index.ts b/packages/forms/src/patterns/address/index.ts index a52d2537..14cb71f3 100644 --- a/packages/forms/src/patterns/address/index.ts +++ b/packages/forms/src/patterns/address/index.ts @@ -5,17 +5,64 @@ import { type Pattern, type PatternConfig, } from '../../pattern'; -import { - type FieldsetProps, - createPromptForPattern, - TextInputProps, -} from '../../components'; +import { PatternProps } from '../../components'; import { safeZodParse } from '../../util/zod'; -import { stateTerritoryOrMilitaryPostAbbreviations } from './jurisdictions'; +import { + stateTerritoryOrMilitaryPostAbbreviations, + stateTerritoryOrMilitaryPostList, +} from './jurisdictions'; import { getFormSessionValue } from '../../session'; export type AddressPattern = Pattern<{}>; +export type AddressComponentProps = PatternProps<{ + childProps: { + streetAddress: { + inputId: string; + value: string; + label: string; + required: boolean; + error?: string; + }; + streetAddress2: { + inputId: string; + value: string; + label: string; + required: boolean; + error?: string; + }; + city: { + inputId: string; + value: string; + label: string; + required: boolean; + error?: string; + }; + stateTerritoryOrMilitaryPost: { + inputId: string; + value: string; + label: string; + required: boolean; + options: typeof stateTerritoryOrMilitaryPostList; + error?: string; + }; + zipCode: { + inputId: string; + value: string; + label: string; + required: boolean; + error?: string; + }; + urbanizationCode: { + inputId: string; + value: string; + label: string; + required: boolean; + error?: string; + }; + }; +}>; + const AddressSchema = z.object({ steetAddress: z.string().max(128), steetAddress2: z.string().max(128).optional(), @@ -46,39 +93,83 @@ export const addressConfig: PatternConfig< return []; }, createPrompt(config, session, pattern, options) { - const extraAttributes: Record = {}; const sessionValue = getFormSessionValue(session, pattern.id); - if (options.validate) { - const isValidResult = validatePattern( - addressConfig, - pattern, - sessionValue - ); - if (!isValidResult.success) { - extraAttributes['error'] = isValidResult.error; - } - } - const children = [ - { - pattern: { - _patternId: pattern.id, - type: 'input', - inputId: pattern.id, - value: sessionValue, - label: 'pattern.data.label', - required: true, - ...extraAttributes, - } as TextInputProps, - children: [], - }, - ]; + const result = options.validate + ? AddressSchema.safeParse(sessionValue) + : null; return { pattern: { - _children: children, _patternId: pattern.id, - type: 'sequence', - }, - children, + type: 'address', + childProps: { + streetAddress: { + inputId: `${pattern.id}.streetAddress`, + value: sessionValue?.streetAddress, + label: 'Street address', + required: true, + error: + !result || result?.success + ? undefined + : result.error.formErrors.fieldErrors.steetAddress?.join(', '), + }, + streetAddress2: { + inputId: `${pattern.id}.streetAddress2`, + value: sessionValue?.streetAddress2, + label: 'Street address line 2', + required: false, + error: + !result || result?.success + ? undefined + : result.error.formErrors.fieldErrors.steetAddress2?.join(', '), + }, + city: { + inputId: `${pattern.id}.city`, + value: sessionValue?.city, + label: 'City', + required: true, + error: + !result || result?.success + ? undefined + : result.error.formErrors.fieldErrors.city?.join(', '), + }, + stateTerritoryOrMilitaryPost: { + inputId: `${pattern.id}.city`, + value: sessionValue?.stateTerritoryOrMilitaryPost, + label: 'State, territory, or military post', + required: true, + options: stateTerritoryOrMilitaryPostList, + error: + !result || result?.success + ? undefined + : result.error.formErrors.fieldErrors.stateTerritoryOrMilitaryPost?.join( + ', ' + ), + }, + zipCode: { + inputId: `${pattern.id}.zipCode`, + value: sessionValue?.zipCode, + label: 'ZIP code', + required: true, + error: + !result || result?.success + ? undefined + : result.error.formErrors.fieldErrors.zipCode?.join(', '), + }, + urbanizationCode: { + inputId: `${pattern.id}.urbanizationCode`, + value: sessionValue?.urbanizationCode, + label: 'Urbanization (Puerto Rico only)', + required: false, + error: + !result || result?.success + ? undefined + : result.error.formErrors.fieldErrors.urbanizationCode?.join( + ', ' + ), + }, + }, + } satisfies AddressComponentProps, + children: [], }; }, }; diff --git a/packages/forms/src/patterns/address/jurisdictions.ts b/packages/forms/src/patterns/address/jurisdictions.ts index ab08fe0d..bbc97ea1 100644 --- a/packages/forms/src/patterns/address/jurisdictions.ts +++ b/packages/forms/src/patterns/address/jurisdictions.ts @@ -126,14 +126,13 @@ export const stateTerritoryOrMilitaryPostAbbreviations = z.union([ z.literal('WY'), z.literal('AA'), z.literal('AE'), - z.literal('AE'), - z.literal('AE'), - z.literal('AE'), z.literal('AP'), ]); +/* type JurisdictionAbbr = (typeof stateTerritoryOrMilitaryPostList)[number]['abbr']; -export const getJurisdictionAbbreviations = (): JurisdictionAbbr[] => +const getJurisdictionAbbreviations = (): JurisdictionAbbr[] => stateTerritoryOrMilitaryPostList.map(j => j.abbr); +*/ diff --git a/packages/forms/src/patterns/fieldset.ts b/packages/forms/src/patterns/fieldset.ts index 9d452144..a6b3cfdd 100644 --- a/packages/forms/src/patterns/fieldset.ts +++ b/packages/forms/src/patterns/fieldset.ts @@ -37,7 +37,6 @@ export const fieldsetConfig: PatternConfig = { }); return { pattern: { - _children: children, _patternId: pattern.id, type: 'fieldset', legend: pattern.data.legend, diff --git a/packages/forms/src/patterns/sequence.ts b/packages/forms/src/patterns/sequence.ts index b09d5ad1..e9245634 100644 --- a/packages/forms/src/patterns/sequence.ts +++ b/packages/forms/src/patterns/sequence.ts @@ -35,7 +35,6 @@ export const sequenceConfig: PatternConfig = { }); return { pattern: { - _children: children, _patternId: pattern.id, type: 'sequence', },