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

Wire drag and drop back up #107

Merged
merged 6 commits into from
Apr 15, 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
6 changes: 3 additions & 3 deletions packages/design/sass/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -464,19 +464,19 @@ iframe:focus {
.grid-col-8 {
width: 100%;
}

.usa-button{
margin-left: 0;
}

.grid-col-4 {
position: static;
}

.form-group-row {
padding: 0 0 1rem;
}

.settingsContainer {
position: fixed;
top: 10%;
Expand Down
18 changes: 12 additions & 6 deletions packages/design/src/AvailableFormList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,27 @@ const FormList = ({
urlForForm: UrlForForm;
}) => {
return (

<table className="usa-table usa-table--stacked">
<caption>Available forms</caption>
<thead>
<tr>
<th className="column1" scope="col">Form title</th>
<th className="column2" scope="col">Description</th>
<th className="column3" scope="col">Actions</th>
<th className="column1" scope="col">
Form title
</th>
<th className="column2" scope="col">
Description
</th>
<th className="column3" scope="col">
Actions
</th>
</tr>
</thead>
<tbody>
{forms.map((form, index) => (
<tr key={index}>
<th data-label="Form title" scope="row">{form.title}</th>
<th data-label="Form title" scope="row">
{form.title}
</th>
<td data-label="Description">{form.description}</td>
<td data-label="Actions">
<a href={urlForForm(form.id)} title={form.title}>
Expand All @@ -58,6 +65,5 @@ const FormList = ({
))}
</tbody>
</table>

);
};
22 changes: 13 additions & 9 deletions packages/design/src/Form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
type FormSession,
type PatternProps,
type Prompt,
type PromptPart,
type PromptComponent,
} from '@atj/forms';

import ActionBar from './ActionBar';
Expand Down Expand Up @@ -232,12 +232,12 @@ export default function Form({
)}

<fieldset className="usa-fieldset">
{prompt.parts.map((part, index) => {
{prompt.components.map((component, index) => {
return (
<PromptComponent
key={index}
context={context}
promptPart={part}
component={component}
/>
);
})}
Expand All @@ -253,17 +253,21 @@ export default function Form({

const PromptComponent = ({
context,
promptPart,
component,
}: {
context: FormUIContext;
promptPart: PromptPart;
component: PromptComponent;
}) => {
const Component = context.components[promptPart.pattern.type];
const Component = context.components[component.props.type];
return (
<Component {...promptPart.pattern}>
{promptPart.children?.map((child, index) => {
<Component {...component.props}>
{component.children?.map((childPromptComponent, index) => {
return (
<PromptComponent key={index} context={context} promptPart={child} />
<PromptComponent
key={index}
context={context}
component={childPromptComponent}
/>
);
})}
</Component>
Expand Down
4 changes: 3 additions & 1 deletion packages/design/src/FormManager/FormDelete/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ export default function FormDelete({
<h1>Delete form</h1>
<h2>Are you sure you want to delete the form with id: `{formId}`?</h2>
<p className="padding-bottom-3">
<button className="usa-button" onClick={deleteForm}>Delete form</button>
<button className="usa-button" onClick={deleteForm}>
Delete form
</button>
</p>
<code>{JSON.stringify(form, null, 4)}</code>
</div>
Expand Down
77 changes: 27 additions & 50 deletions packages/design/src/FormManager/FormEdit/DraggableList.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { Children, useState } from 'react';
import React, { Children } from 'react';
import {
DndContext,
closestCenter,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
UniqueIdentifier,
} from '@dnd-kit/core';
import {
arrayMove,
Expand All @@ -16,32 +17,24 @@ import {
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';

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

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

const SortableItem = ({
id,
children,
}: {
id: string;
id: UniqueIdentifier;
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
ref={setNodeRef}
style={{
transform: CSS.Transform.toString(transform),
transition,
}}
>
<div className="editFieldsRowWrapper grid-row grid-gap">
<div
className="editPageGrabButtonWrapper grid-col-1 grid-col"
Expand All @@ -54,32 +47,26 @@ const SortableItem = ({
</div>
<div className="editFieldsWrapper grid-col-11 grid-col">{children}</div>
</div>
</li>
</div>
);
};

type DraggableListProps = React.PropsWithChildren<{
pattern: Pattern<SequencePattern>;
form: Blueprint;
setSelectedPattern: (pattern: Pattern) => void;
order: UniqueIdentifier[];
updateOrder: (order: UniqueIdentifier[]) => void;
}>;
export const DraggableList: React.FC<DraggableListProps> = ({
pattern,
form,
setSelectedPattern,
children,
order,
updateOrder,
}) => {
const [patterns, setPatterns] = useState<Pattern[]>(
pattern.data.patterns.map((patternId: PatternId) => {
return getPattern(form, patternId);
})
);
const sensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
);

const arrayChildren = Children.toArray(children);

return (
<DndContext
sensors={sensors}
Expand All @@ -90,32 +77,22 @@ export const DraggableList: React.FC<DraggableListProps> = ({
return;
}
if (active.id !== over.id) {
const oldIndex = patterns.findIndex(pattern => {
return pattern.id === active.id;
});
const newIndex = patterns.findIndex(pattern => {
return pattern.id === over.id;
});
const newOrder = arrayMove(patterns, oldIndex, newIndex);
setPatterns(newOrder);
setSelectedPattern({
id: pattern.id,
type: pattern.type,
data: {
patterns: newOrder.map(pattern => pattern.id),
},
} satisfies SequencePattern);
const oldIndex = order.indexOf(active.id);
const newIndex = order.indexOf(over.id);
const newOrder = arrayMove(order, oldIndex, newIndex);
updateOrder(newOrder);
}
}}
>
<SortableContext items={patterns} strategy={verticalListSortingStrategy}>
<ul className="editFormWrapper">
{arrayChildren.map((child, index) => (
<SortableItem key={index} id={patterns[index].id}>
<SortableContext items={order} strategy={verticalListSortingStrategy}>
{arrayChildren.map((child, index) => {
const patternId = order[index];
return (
<SortableItem key={patternId} id={patternId}>
{child}
</SortableItem>
))}
</ul>
);
})}
</SortableContext>
</DndContext>
);
Expand Down
47 changes: 41 additions & 6 deletions packages/design/src/FormManager/FormEdit/FormEdit.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export default {

export const FormEditTest: StoryObj<typeof FormEdit> = {
play: async ({ canvasElement }) => {
await editFieldLabel(canvasElement, 0, 'First field label');
await editFieldLabel(canvasElement, 1, 'Second field label');
await editFieldLabel(canvasElement, 1, 'First field label');
await editFieldLabel(canvasElement, 2, 'Second field label');
},
};

Expand All @@ -40,20 +40,53 @@ export const FormEditAddPattern: StoryObj<typeof FormEdit> = {
const canvas = within(canvasElement);

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

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

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

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

// This test only works in a real browser, not via JSDOM as we use it.
/*
export const FormEditReorderPattern: StoryObj<typeof FormEdit> = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);

const grabber = await canvas.getAllByText(':::')[0];
await grabber.focus();

// Enter reordering mode with the spacebar
await userEvent.type(grabber, ' ');
await new Promise(r => setTimeout(r, 100));

// Press the arrow down, to move the first pattern to the second position
await userEvent.type(grabber, '[ArrowDown]');
await new Promise(r => setTimeout(r, 100));

// Press the spacebar to exit reordering mode
await userEvent.type(grabber, ' ');
await new Promise(r => setTimeout(r, 100));

// Pattern 1 should be after pattern 2 in the document
const pattern1 = canvas.getByText('Pattern 1');
const pattern2 = canvas.getByText('Pattern 2');
expect(pattern2.compareDocumentPosition(pattern1)).toBe(
Node.DOCUMENT_POSITION_FOLLOWING
);
},
};
*/

const editFieldLabel = async (
element: HTMLElement,
buttonIndex: number,
Expand All @@ -62,7 +95,9 @@ const editFieldLabel = async (
const canvas = within(element);

// Click "edit form" button for first field
await userEvent.click(canvas.getAllByRole('button')[buttonIndex]);
await userEvent.click(
(await canvas.findAllByLabelText('Edit form group'))[buttonIndex]
);

// Enter new text for first field
const input = canvas.getByLabelText('Field label');
Expand Down
Loading
Loading