Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Naming and organization improvements #85

Merged
merged 18 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading