Skip to content

Commit

Permalink
Naming and organization improvements (#85)
Browse files Browse the repository at this point in the history
* flatten location of form elements

* Move form element definitions

* Rename UI component types

* Rename Pattern -> PatternProps

* Rename FormElement -> Pattern

* Rename FormDefinition -> BluePrint

* Rename FormSession -> Session.
Also, create preview components once when initializing the FormEditProvider, rather than on every render.

* Remove required attribute

* Revert "Rename FormSession -> Session."

This reverts commit 746802a.

* Rename Pattern.default to Pattern.initial

* Remove ambiguity around createSession / createFormSession

* Rename forms/elements to forms/patterns

* Rename forms/pattern.ts to forms/components.ts

* To avoid confusion, remove "Pattern" from the name of component props.

* Use PatternProps at the point of component prop definition, rather than where comsumed.

* Update README with terminology

* Rename forms/element.ts to forms/pattern.ts

* Replace additional references to "element" with "pattern"
  • Loading branch information
danielnaab authored Mar 27, 2024
1 parent 6b337b9 commit afd950d
Show file tree
Hide file tree
Showing 68 changed files with 1,069 additions and 1,152 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,23 @@ Additional documentation:
- [Architectural Decision Records (ADRs)](./documents/adr/)
- [Non-project contributions](./documents/value-created-log.md)

## Overview

The platform is made up of the following high-level terms.

### Key personas

- Content authors: legal experts who craft guided interview experiences via a "no code" interface
- Self-represented litigants (SREs): end-users who interact with the court via guided interviews created by content authors

### Things

- **Blueprint**: produced by a content author, the blueprint defines the structure of an interactive session between a court and an SRL
- **Conversation**: one instance of a blueprint; the interactive session between a court and an SRL. Other terms for this concept include dialogue or session.
- **Pattern**: the building blocks of a blueprint, patterns implement UX best-practices, defining the content and behavior of the user interface.
- **Prompt**: produced by a pattern, the prompt defines what is presented to the end user at single point in a conversation.
- **Component**: user interface component that acts as the building block of prompts.

## Development

This project uses [pnpm workspaces](https://pnpm.io/workspaces). To work with this project, [install pnpm](https://pnpm.io/installation) and then the project dependencies:
Expand Down
2 changes: 1 addition & 1 deletion apps/doj-demo/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import react from '@astrojs/react';
// https://astro.build/config
export default defineConfig({
output: 'server',
trailingSlash: 'never',
trailingSlash: 'always',
base: addTrailingSlash(process.env.BASEURL || ''),
adapter: node({
mode: 'standalone',
Expand Down
8 changes: 4 additions & 4 deletions apps/doj-demo/src/components/AppFormManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React from 'react';

import {
FormManager,
defaultFormElementComponents,
defaultFormElementEditComponents,
defaultPatternComponents,
defaultPatternEditComponents,
} from '@atj/design';

import { getAppContext } from '../context';
Expand All @@ -14,8 +14,8 @@ export default function () {
<FormManager
context={{
config: ctx.formConfig,
components: defaultFormElementComponents,
editComponents: defaultFormElementEditComponents,
components: defaultPatternComponents,
editComponents: defaultPatternEditComponents,
uswdsRoot: ctx.uswdsRoot,
}}
formService={ctx.formService}
Expand Down
4 changes: 2 additions & 2 deletions apps/doj-demo/src/components/AppFormRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';

import { FormRouter, defaultFormElementComponents } from '@atj/design';
import { FormRouter, defaultPatternComponents } from '@atj/design';
import { getAppContext } from '../context';

export default function AppFormRouter() {
Expand All @@ -9,7 +9,7 @@ export default function AppFormRouter() {
<FormRouter
context={{
config: ctx.formConfig,
components: defaultFormElementComponents,
components: defaultPatternComponents,
uswdsRoot: ctx.uswdsRoot,
}}
formService={ctx.formService}
Expand Down
8 changes: 4 additions & 4 deletions apps/spotlight/src/components/AppFormManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React from 'react';

import {
FormManager,
defaultFormElementComponents,
defaultFormElementEditComponents,
defaultPatternComponents,
defaultPatternEditComponents,
} from '@atj/design';

import { getAppContext } from '../context';
Expand All @@ -14,8 +14,8 @@ export default function () {
<FormManager
context={{
config: ctx.formConfig,
components: defaultFormElementComponents,
editComponents: defaultFormElementEditComponents,
components: defaultPatternComponents,
editComponents: defaultPatternEditComponents,
uswdsRoot: ctx.uswdsRoot,
}}
formService={ctx.formService}
Expand Down
4 changes: 2 additions & 2 deletions apps/spotlight/src/components/AppFormRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';

import { FormRouter, defaultFormElementComponents } from '@atj/design';
import { FormRouter, defaultPatternComponents } from '@atj/design';
import { getAppContext } from '../context';

export default function AppFormRouter() {
Expand All @@ -9,7 +9,7 @@ export default function AppFormRouter() {
<FormRouter
context={{
config: ctx.formConfig,
components: defaultFormElementComponents,
components: defaultPatternComponents,
uswdsRoot: ctx.uswdsRoot,
}}
formService={ctx.formService}
Expand Down
11 changes: 5 additions & 6 deletions packages/design/src/Form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
createPrompt,
type FormConfig,
type FormSession,
type Pattern,
type PatternProps,
type Prompt,
type PromptPart,
} from '@atj/forms';
Expand All @@ -20,12 +20,11 @@ export type FormUIContext = {
uswdsRoot: `${string}/`;
};

export type ComponentForPattern<T extends Pattern = Pattern<unknown>> = Record<
string,
FormElementComponent<T>
>;
export type ComponentForPattern<
T extends PatternProps = PatternProps<unknown>,
> = Record<string, PatternComponent<T>>;

export type FormElementComponent<T extends Pattern = Pattern<unknown>> =
export type PatternComponent<T extends PatternProps = PatternProps<unknown>> =
React.ComponentType<{
pattern: T;
children?: React.ReactNode;
Expand Down
10 changes: 5 additions & 5 deletions packages/design/src/FormManager/DocumentImporter/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom';

import {
type DocumentFieldMap,
type FormDefinition,
type Blueprint,
SAMPLE_DOCUMENTS,
addDocument,
addDocumentFieldsToForm,
Expand All @@ -24,7 +24,7 @@ const DocumentImporter = ({
baseUrl: string;
formId: string;
context: FormUIContext;
form: FormDefinition;
form: Blueprint;
formService: FormService;
}) => {
const { state, actions } = useDocumentImporter(formService, form, baseUrl);
Expand Down Expand Up @@ -184,13 +184,13 @@ const DocumentImporter = ({

type State = {
page: number;
previewForm: FormDefinition;
previewForm: Blueprint;
documentFields?: DocumentFieldMap;
};

const useDocumentImporter = (
formService: FormService,
form: FormDefinition,
form: Blueprint,
baseUrl: string
) => {
const navigate = useNavigate();
Expand All @@ -203,7 +203,7 @@ const useDocumentImporter = (
data: {
path: string;
fields: DocumentFieldMap;
previewForm: FormDefinition;
previewForm: Blueprint;
};
}
| {
Expand Down
57 changes: 28 additions & 29 deletions packages/design/src/FormManager/FormEdit/DraggableList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ import {
import { CSS } from '@dnd-kit/utilities';

import {
getFormElement,
type FormDefinition,
type FormElement,
FormElementId,
getPattern,
type Blueprint,
type Pattern,
PatternId,
} from '@atj/forms';

import { SequenceElement } from '@atj/forms/src/config/elements/sequence';
import { SequencePattern } from '@atj/forms/src/patterns/sequence';

const SortableItem = ({
id,
Expand Down Expand Up @@ -59,19 +59,19 @@ const SortableItem = ({
};

type DraggableListProps = React.PropsWithChildren<{
element: FormElement<SequenceElement>;
form: FormDefinition;
setSelectedElement: (element: FormElement) => void;
pattern: Pattern<SequencePattern>;
form: Blueprint;
setSelectedPattern: (pattern: Pattern) => void;
}>;
export const DraggableList: React.FC<DraggableListProps> = ({
element,
pattern,
form,
setSelectedElement,
setSelectedPattern,
children,
}) => {
const [elements, setElements] = useState<FormElement[]>(
element.data.elements.map((elementId: FormElementId) => {
return getFormElement(form, elementId);
const [patterns, setPatterns] = useState<Pattern[]>(
pattern.data.patterns.map((patternId: PatternId) => {
return getPattern(form, patternId);
})
);
const sensors = useSensors(
Expand All @@ -90,32 +90,31 @@ export const DraggableList: React.FC<DraggableListProps> = ({
return;
}
if (active.id !== over.id) {
const oldIndex = elements.findIndex(element => {
return element.id === active.id;
const oldIndex = patterns.findIndex(pattern => {
return pattern.id === active.id;
});
const newIndex = elements.findIndex(element => {
return element.id === over.id;
const newIndex = patterns.findIndex(pattern => {
return pattern.id === over.id;
});
const newOrder = arrayMove(elements, oldIndex, newIndex);
setElements(newOrder);
setSelectedElement({
id: element.id,
type: element.type,
const newOrder = arrayMove(patterns, oldIndex, newIndex);
setPatterns(newOrder);
setSelectedPattern({
id: pattern.id,
type: pattern.type,
data: {
elements: newOrder.map(element => element.id),
patterns: newOrder.map(pattern => pattern.id),
},
default: {
elements: [],
initial: {
patterns: [],
},
required: element.required,
} satisfies SequenceElement);
} satisfies SequencePattern);
}
}}
>
<SortableContext items={elements} strategy={verticalListSortingStrategy}>
<SortableContext items={patterns} strategy={verticalListSortingStrategy}>
<ul className="editFormWrapper">
{arrayChildren.map((child, index) => (
<SortableItem key={index} id={elements[index].id}>
<SortableItem key={index} id={patterns[index].id}>
{child}
</SortableItem>
))}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,45 +1,45 @@
import React, { useEffect, useRef } from 'react';
import { FormProvider, useForm } from 'react-hook-form';

import { type FormElementMap } from '@atj/forms';
import { type PatternMap } from '@atj/forms';
import { useFormEditStore } from './store';

export const FormElementEdit = () => {
export const PatternEdit = () => {
const context = useFormEditStore(state => state.context);
const form = useFormEditStore(state => state.form);
const selectedElement = useFormEditStore(state => state.selectedElement);
const { setSelectedElement, updateSelectedFormElement } = useFormEditStore(
const selectedPattern = useFormEditStore(state => state.selectedPattern);
const { setSelectedPattern, updateSelectedPattern } = useFormEditStore(
state => ({
setSelectedElement: state.setSelectedElement,
updateSelectedFormElement: state.updateSelectedFormElement,
setSelectedPattern: state.setSelectedPattern,
updateSelectedPattern: state.updateSelectedPattern,
})
);

const methods = useForm<FormElementMap>({
defaultValues: selectedElement
const methods = useForm<PatternMap>({
defaultValues: selectedPattern
? {
[selectedElement.id]: selectedElement,
[selectedPattern.id]: selectedPattern,
}
: {},
});
const settingsContainerRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (selectedElement === undefined) {
if (selectedPattern === undefined) {
return;
}
methods.reset();
methods.setValue(selectedElement.id, selectedElement);
}, [selectedElement]);
methods.setValue(selectedPattern.id, selectedPattern);
}, [selectedPattern]);

// Updates the scroll position of the edit form when it's visible
useEffect(() => {
let frameId: number;
const updatePosition = () => {
if (window.innerWidth > 879) {
if (selectedElement) {
if (selectedPattern) {
const element = document.querySelector(
`[data-id="${selectedElement.id}"]`
`[data-id="${selectedPattern.id}"]`
);
if (element && settingsContainerRef.current) {
const rect = element.getBoundingClientRect();
Expand All @@ -53,13 +53,13 @@ export const FormElementEdit = () => {
return () => {
cancelAnimationFrame(frameId);
};
}, [selectedElement]);
}, [selectedPattern]);

if (!selectedElement) {
if (!selectedPattern) {
return;
}

const SelectedEditComponent = context.editComponents[selectedElement.type];
const SelectedEditComponent = context.editComponents[selectedPattern.type];
return (
<FormProvider {...methods}>
<div
Expand All @@ -69,19 +69,19 @@ export const FormElementEdit = () => {
<form
className="editForm"
onSubmit={methods.handleSubmit(formData => {
updateSelectedFormElement(formData);
updateSelectedPattern(formData);
})}
>
<h3>Editing &quot;{selectedElement.data.label}&quot;...</h3>
<h3>Editing &quot;{selectedPattern.data.label}&quot;...</h3>
<SelectedEditComponent
context={context}
form={form}
element={selectedElement}
pattern={selectedPattern}
/>
<p>
<input className="usa-button" type="submit" value="Save" />
<input
onClick={() => setSelectedElement(undefined)}
onClick={() => setSelectedPattern(undefined)}
className="usa-button close-button"
type="submit"
value="Cancel"
Expand Down
Loading

0 comments on commit afd950d

Please sign in to comment.