Skip to content

Commit

Permalink
Merge jim/api-setup-2 into this branch
Browse files Browse the repository at this point in the history
  • Loading branch information
danielnaab committed Apr 4, 2024
1 parent 4d7168e commit a1e0ece
Show file tree
Hide file tree
Showing 85 changed files with 1,994 additions and 1,595 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
2 changes: 1 addition & 1 deletion packages/design/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"vite": "^5.0.12",
"vite": "^5.0.13",
"vite-plugin-dts": "^3.7.1",
"wait-on": "^7.2.0"
},
Expand Down
22 changes: 11 additions & 11 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,16 +20,16 @@ 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>> =
React.ComponentType<{
pattern: T;
children?: React.ReactNode;
}>;
export type PatternComponent<T extends PatternProps = PatternProps<unknown>> =
React.ComponentType<
T & {
children?: React.ReactNode;
}
>;

const usePrompt = (
initialPrompt: Prompt,
Expand Down Expand Up @@ -260,7 +260,7 @@ const PromptComponent = ({
}) => {
const Component = context.components[promptPart.pattern.type];
return (
<Component pattern={promptPart.pattern}>
<Component {...promptPart.pattern}>
{promptPart.children?.map((child, index) => {
return (
<PromptComponent key={index} context={context} promptPart={child} />
Expand Down
16 changes: 8 additions & 8 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 @@ -12,7 +12,7 @@ import {
import { type FormService } from '@atj/form-service';

import Form, { FormUIContext } from '../../Form';
import { onFileInputChangeGetFile } from '../FormList/PDFFileSelect/file-input';
import { onFileInputChangeGetFile } from '../FormList/CreateNew/file-input';

const DocumentImporter = ({
baseUrl,
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 @@ -69,7 +69,7 @@ const DocumentImporter = ({
}
};

const PDFFileSelect = () => {
const CreateNew = () => {
return (
<div className="usa-form-group">
<label
Expand Down Expand Up @@ -175,7 +175,7 @@ const DocumentImporter = ({
<Step title="Preview form" step={3} current={state.page} />
</ol>
</div>
{state.page === 1 && <PDFFileSelect />}
{state.page === 1 && <CreateNew />}
{state.page === 2 && <BuildFormPage />}
{state.page === 3 && <PreviewFormPage />}
</div>
Expand All @@ -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
31 changes: 31 additions & 0 deletions packages/design/src/FormManager/FormEdit/AddPattern.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import { useFormEditStore } from './store';

export const AddPattern = () => {
const store = useFormEditStore(state => ({
availablePatterns: state.availablePatterns,
addPattern: state.addPattern,
}));

return (
<fieldset>
<label className="usa-label">
Add a pattern
<select
className="usa-select"
onChange={event => {
store.addPattern(event.target.value);
event.target.selectedIndex = 0;
}}
>
<option></option>
{store.availablePatterns.map((pattern, index) => (
<option key={index} value={pattern.patternType}>
{pattern.displayName}
</option>
))}
</select>
</label>
</fieldset>
);
};
56 changes: 26 additions & 30 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,28 @@ 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: [],
},
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
19 changes: 19 additions & 0 deletions packages/design/src/FormManager/FormEdit/FormEdit.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,25 @@ export const FormEditTest: StoryObj<typeof FormEdit> = {
},
};

export const FormEditAddPattern: StoryObj<typeof FormEdit> = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);

// Select the first pattern for editing
const button = (await canvas.findAllByRole('button'))[0];
await userEvent.click(button);

// Get the initial count of inputs
const initialCount = (await canvas.getAllByRole('textbox')).length;

const select = canvas.getByLabelText('Add a pattern');
await userEvent.selectOptions(select, 'Text input');

const finalCount = (await canvas.getAllByRole('textbox')).length;
expect(finalCount).toEqual(initialCount + 1);
},
};

const editFieldLabel = async (
element: HTMLElement,
buttonIndex: number,
Expand Down
Loading

0 comments on commit a1e0ece

Please sign in to comment.