Skip to content

Commit

Permalink
Preview edit updates (#53)
Browse files Browse the repository at this point in the history
* Save working in the preview view, but not updating the UI yet

* Update notion of a "prompt part" to include {prompt, children}

* In-progress

* Add output document to the mock form

* - PDF field mappings
- working on drag/drop for preview page

* Make FormElementValue a generic that takes FormElement as a parameter

* Support grouping in the PDF import, and remove redundant input text attributes.

* Linting

* Fieldset legends

* Add PDF field mapping for the AL form, to map the PDFLib fields to the extracted data
  • Loading branch information
danielnaab authored Feb 29, 2024
1 parent b0a85af commit 96d315d
Show file tree
Hide file tree
Showing 40 changed files with 891 additions and 301 deletions.
1 change: 1 addition & 0 deletions apps/spotlight/public/uswds
1 change: 1 addition & 0 deletions apps/spotlight/src/components/AppFormManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default function () {
config: ctx.formConfig,
components: defaultFormElementComponents,
editComponents: defaultFormElementEditComponents,
uswdsRoot: ctx.uswdsRoot,
}}
formService={ctx.formService}
baseUrl={ctx.baseUrl}
Expand Down
1 change: 1 addition & 0 deletions apps/spotlight/src/components/AppFormRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default function AppFormRouter() {
context={{
config: ctx.formConfig,
components: defaultFormElementComponents,
uswdsRoot: ctx.uswdsRoot,
}}
formService={ctx.formService}
/>
Expand Down
4 changes: 3 additions & 1 deletion apps/spotlight/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export type AppContext = {
github: GithubRepository;
formConfig: FormConfig;
formService: FormService;
uswdsRoot: `${string}/`;
};

let _context: AppContext | null = null;
Expand All @@ -24,12 +25,13 @@ export const getAppContext = (): AppContext => {
return _context;
};

const createAppContext = (env: any) => {
const createAppContext = (env: any): AppContext => {
return {
github: env.GITHUB,
baseUrl: env.BASE_URL,
formConfig: defaultFormConfig,
formService: createAppFormService(),
uswdsRoot: `${env.BASE_URL}uswds/`,
};
};

Expand Down
7 changes: 5 additions & 2 deletions apps/spotlight/src/layouts/Layout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ const context = getAppContext();
/>
<link href="/favicon/favicon.ico" rel="shortcut icon" />
<meta name="generator" content={Astro.generator} />
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link
href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet"
/>
<script src="../lib/initialize.ts"></script>
<title>{title}</title>
</head>
Expand All @@ -54,7 +57,7 @@ const context = getAppContext();
<UsaBanner />
<Header />
<slot />
<Footer github={context.github} />
<Footer github={context.github} />
</div>
</body>
</html>
1 change: 1 addition & 0 deletions packages/design/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ const config: StorybookConfig = {
docs: {
autodocs: 'tag',
},
staticDirs: ['../static'],
};
export default config;
22 changes: 11 additions & 11 deletions packages/design/sass/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

h1 {
font-size: 2.5em;

span {
display: inline-block;
vertical-align: top;
Expand Down Expand Up @@ -51,11 +51,11 @@ main {
max-width: 75rem;
}

.usa-combo-box__input,
.usa-input,
.usa-input-group,
.usa-range,
.usa-select,
.usa-combo-box__input,
.usa-input,
.usa-input-group,
.usa-range,
.usa-select,
.usa-textarea {
max-width: 40rem;
height: 3rem;
Expand Down Expand Up @@ -166,7 +166,7 @@ main {
padding: 15px;
font-size: 20px;
text-align: center;

&:hover {
background: #f5f2ff;
border: 1px solid #e4deff;
Expand All @@ -187,9 +187,9 @@ main {
}
}

[contentEditable=true]:focus,
[href]:focus,
[tabindex]:focus,
[contentEditable=true]:focus,
[href]:focus,
[tabindex]:focus,
iframe:focus {
outline: 0.25rem solid #783cb9;
}
Expand Down Expand Up @@ -218,7 +218,7 @@ iframe:focus {
.edit-button-icon {
padding: 0 10px;
flex: none;
}
}
}

&.field-selected {
Expand Down
39 changes: 32 additions & 7 deletions packages/design/src/Form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ import {
createPrompt,
type FormConfig,
type FormSession,
type Prompt,
type Pattern,
type Prompt,
type PromptPart,
} from '@atj/forms';

import ActionBar from './ActionBar';

export type FormUIContext = {
config: FormConfig;
components: ComponentForPattern;
uswdsRoot: `${string}/`;
};

export type ComponentForPattern<T extends Pattern = Pattern<unknown>> = Record<
Expand All @@ -26,6 +28,7 @@ export type ComponentForPattern<T extends Pattern = Pattern<unknown>> = Record<
export type FormElementComponent<T extends Pattern = Pattern<unknown>> =
React.ComponentType<{
pattern: T;
children?: React.ReactNode;
}>;

const usePrompt = (
Expand Down Expand Up @@ -101,16 +104,38 @@ export default function Form({
})}
>
<fieldset className="usa-fieldset">
{prompt.parts
.map((pattern, index) => {
const Component = context.components[pattern.type];
return <Component key={index} pattern={pattern} />;
})
.filter(a => a)}
{prompt.parts.map((part, index) => {
return (
<PromptComponent
key={index}
context={context}
promptPart={part}
/>
);
})}
{/* Add submit button or other controls as needed */}
</fieldset>
<ActionBar actions={prompt.actions} />
</form>
</FormProvider>
);
}

const PromptComponent = ({
context,
promptPart,
}: {
context: FormUIContext;
promptPart: PromptPart;
}) => {
const Component = context.components[promptPart.pattern.type];
return (
<Component pattern={promptPart.pattern}>
{promptPart.children?.map((child, index) => {
return (
<PromptComponent key={index} context={context} promptPart={child} />
);
})}
</Component>
);
};
3 changes: 1 addition & 2 deletions packages/design/src/FormManager/DocumentImporter/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ import {
createFormSession,
} from '@atj/forms';

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

const DocumentImporter = ({
baseUrl,
Expand Down
128 changes: 128 additions & 0 deletions packages/design/src/FormManager/FormEdit/DraggableList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import React, { Children, useState } from 'react';
import {
DndContext,
closestCenter,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
} from '@dnd-kit/core';
import {
arrayMove,
SortableContext,
sortableKeyboardCoordinates,
useSortable,
verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';

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

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

const SortableItem = ({
id,
children,
}: {
id: string;
children: React.ReactNode;
}) => {
const { attributes, listeners, setNodeRef, transform, transition } =
useSortable({ id });

const style = {
transform: CSS.Transform.toString(transform),
transition,
};

return (
<li ref={setNodeRef} style={style}>
<div className="editFieldsRowWrapper grid-row grid-gap">
<div
className="editPageGrabButtonWrapper grid-col-1 grid-col"
{...listeners}
{...attributes}
style={{ cursor: 'grab' }}
>
<span className="grabber1">:::</span>
<span className="grabber2">:::</span>
</div>
<div className="editFieldsWrapper grid-col-11 grid-col">{children}</div>
</div>
</li>
);
};

type DraggableListProps = React.PropsWithChildren<{
element: FormElement<SequenceElement>;
form: FormDefinition;
setSelectedElement: (element: FormElement) => void;
}>;
export const DraggableList: React.FC<DraggableListProps> = ({
element,
form,
setSelectedElement,
children,
}) => {
const [elements, setElements] = useState<FormElement[]>(
element.data.elements.map((elementId: FormElementId) => {
return getFormElement(form, elementId);
})
);
const sensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
);

const arrayChildren = Children.toArray(children);
return (
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragEnd={event => {
const { active, over } = event;
if (over === null) {
return;
}
if (active.id !== over.id) {
const oldIndex = elements.findIndex(element => {
return element.id === active.id;
});
const newIndex = elements.findIndex(element => {
return element.id === over.id;
});
const newOrder = arrayMove(elements, oldIndex, newIndex);
setElements(newOrder);
setSelectedElement({
id: element.id,
type: element.type,
data: {
elements: newOrder.map(element => element.id),
},
default: {
elements: [],
},
required: element.required,
} satisfies SequenceElement);
}
}}
>
<SortableContext items={elements} strategy={verticalListSortingStrategy}>
<ul className="editFormWrapper">
{arrayChildren.map((child, index) => (
<SortableItem key={index} id={elements[index].id}>
{child}
</SortableItem>
))}
</ul>
</SortableContext>
</DndContext>
);
};

export default DraggableList;
Loading

0 comments on commit 96d315d

Please sign in to comment.