Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into login-gov
Browse files Browse the repository at this point in the history
  • Loading branch information
danielnaab committed Aug 7, 2024
2 parents 76c2757 + ecd2f0c commit 6cda340
Show file tree
Hide file tree
Showing 12 changed files with 453 additions and 56 deletions.
64 changes: 64 additions & 0 deletions .github/ISSUE_TEMPLATE/story.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
name: Story
about: Suggest a user story for this product
title: ''
labels: story
assignees: ''
projects: ['GSA-TTS/17']
---

## Overview

As a _, I would like _, so that I can _.

## Context

*Optional: Any reference material or thoughts we may need for later reference, or assumptions of prior or future work that's out of scope for this story.*

- [ ]

## Acceptance Criteria

*Required outcomes of the story*

- [ ]

## Research Questions

- *Optional: Any initial questions for research*

## Tasks

*Research, design, and engineering work needed to complete the story.*

- [ ]

## Definition of done

The "definition of done" ensures our quality standards are met with each bit of user-facing behavior we add. Everything that can be done incrementally should be done incrementally, while the context and details are fresh. If it’s inefficient or “hard” to do so, the team should figure out why and add OPEX/DEVEX backlog items to make it easier and more efficient.

- [ ] Behavior
- [ ] Acceptance criteria met
- [ ] Implementation matches design decisions
- [ ] Documentation
- [ ] ADRs (`/documents/adr` folder)
- [ ] Relevant `README.md`(s)
- [ ] Code quality
- [ ] Code refactored for clarity and no design/technical debt
- [ ] Adhere to separation of concerns; code is not tightly coupled, especially to 3rd party dependencies; dependency rule followed
- [ ] Code is reviewed by team member
- [ ] Code quality checks passed
- [ ] Security and privacy
- [ ] Automated security and privacy gates passed
- [ ] Testing tasks completed
- [ ] Automated tests pass
- [ ] Unit test coverage of our code >= 90%
- [ ] Build and deploy
- [ ] Build process updated
- [ ] API(s) are versioned
- [ ] Feature toggles created and/or deleted. Document the feature toggle
- [ ] Source code is merged to the main branch

## Decisions

- *Optional: Any decisions we've made while working on this story*
6 changes: 3 additions & 3 deletions .github/workflows/_validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ jobs:
run: echo ::set-output name=NODE_VERSION::$(cat .nvmrc)

- name: Install required node.js version
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ steps.nvmrc.outputs.NODE_VERSION }}

- name: Install pnpm
uses: pnpm/action-setup@v4
id: pnpm-install
with:
version: 8
version: 9
run_install: false

- name: Get pnpm store directory
Expand All @@ -34,7 +34,7 @@ jobs:
run: |
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
Expand Down
5 changes: 2 additions & 3 deletions e2e/src/create.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { GuidedFormCreation, Create } from '../../packages/design/src/FormManage
import { BASE_URL } from './constants';
import { pathToRegexp } from 'path-to-regexp';


const createNewForm = async (page: Page) => {
await page.goto(`${BASE_URL}/${GuidedFormCreation.getUrl()}`);
await page.getByRole('button', { name: 'Create New' }).click();
Expand Down Expand Up @@ -54,7 +53,7 @@ test('Add questions', async ({ page }) => {

});

test('Drag-and-drop via mouse interaction', async ({ page }) => {
test('Drag-and-drop questions via mouse interaction', async ({ page }) => {
await createNewForm(page);
await addQuestions(page);

Expand All @@ -73,4 +72,4 @@ test('Drag-and-drop via mouse interaction', async ({ page }) => {

await expect(page.locator('.draggable-list-item-wrapper').nth(1)).toContainText('Field label');

});
});
117 changes: 117 additions & 0 deletions e2e/src/edit.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { expect, test, Page } from '@playwright/test';
import { BASE_URL } from './constants';

interface PageDataTest {
title: string;
pattern?: Array<string>;
}

class TestPage {
page: Page;
constructor(page: Page) {
this.page = page;
}
async setLocalStorage(key: string, value: any) {
await this.page.context().addInitScript(([key, value]) => {
localStorage.setItem(key, value);
}, [key, JSON.stringify(value)]);
}
async moveListItem(buttonText: string, pageTitles: string[]) {
const handle = this.page.locator('li').filter({ hasText: `${buttonText}${pageTitles[0]}` }).getByRole('button');
await handle.hover();
await this.page.mouse.down();
const nextElement = this.page.locator('li').filter({ hasText: `${buttonText}${pageTitles[1]}` }).getByRole('button');
await nextElement.hover();
await this.page.mouse.up();
}
async checkFirstUrl(path: string) {
const firstUrl = new URL(this.page.url());
expect(firstUrl.hash.indexOf(path)).toEqual(-1);
}
async checkNextUrl(path: string) {
const nextUrl = new URL(this.page.url());
expect(nextUrl.hash.indexOf(path)).not.toEqual(-1);
}
}

const preparePageTitles = (items: any) => {
return Object.values(items).filter(item => item.type === 'page').map(item => (item.data as PageDataTest).title);
}

const prepareUpdatedPageTitles = (items: string[]) => {
const newFirstItem = items.shift();
return items.splice(1, 0, newFirstItem || '');
}

test('Drag-and-drop pages via mouse interaction', async ({ context, page }) => {
const key = '62ba7264-e869-40fc-ac68-5892f1228a9b';
const obj = {
"summary": {
"title": "My form - 2024-07-10T20:20:08.037Z",
"description": ""
},
"root": "root",
"patterns": {
"root": {
"type": "page-set",
"id": "root",
"data": {
"pages": [
"cbd810ef-fd29-48f6-901b-fa88ab4f4100",
"5e4381b5-c05e-4471-ba1c-cd4ff045ab74",
"aca1a6a4-e7ba-4f37-bdf1-2726aa40831a"
]
}
},
"cbd810ef-fd29-48f6-901b-fa88ab4f4100": {
"type": "page",
"id": "cbd810ef-fd29-48f6-901b-fa88ab4f4100",
"data": {
"title": "Page 1",
"patterns": []
}
},
"5e4381b5-c05e-4471-ba1c-cd4ff045ab74": {
"id": "5e4381b5-c05e-4471-ba1c-cd4ff045ab74",
"type": "page",
"data": {
"title": "A new page",
"patterns": []
}
},
"aca1a6a4-e7ba-4f37-bdf1-2726aa40831a": {
"id": "aca1a6a4-e7ba-4f37-bdf1-2726aa40831a",
"type": "page",
"data": {
"title": "Different Page",
"patterns": []
}
}
},
"outputs": []
};
const testPage = new TestPage(page);
await testPage.setLocalStorage(key, obj);
const pageTitles = preparePageTitles(obj.patterns);

await page.goto(`${BASE_URL}`);
await page.getByRole('link', { name: 'Edit' }).click();
const buttonText = 'Move this item';

await testPage.checkFirstUrl('?page=');
await testPage.moveListItem(buttonText, pageTitles);

await page.waitForFunction(([pageTitles, buttonText]) => {
const items = document.querySelectorAll('.usa-sidenav .draggable-list-item-wrapper');
return (items[0] as HTMLElement).innerText === buttonText + '\n' + pageTitles[1] && items.length === 3;
}, [pageTitles, buttonText]);

const pageTitlesCopy = [...pageTitles];
const newPageTitles = prepareUpdatedPageTitles(pageTitlesCopy);

const reorderedFirst = page.locator('ul').filter({ hasText: buttonText + newPageTitles.join(buttonText) }).getByRole('button').first();

await expect(reorderedFirst).toBeVisible();
await testPage.checkNextUrl('?page=1');

});
6 changes: 6 additions & 0 deletions packages/design/sass/_uswds-custom-styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ main {
}
}
}
.draggable-list-item-wrapper {
background-color: color('primary-lighter');
.usa-sidenav__item {
border-top: none;
}
}
}

.usa-section {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,9 @@ export const PageMenu = ({ pages }: PageMenuProps) => {
{pages.map((page, index) => (
<li
key={index}
className={classNames(
'usa-sidenav__item tablet:margin-left-0 margin-left-2',
styles.sideNav,
{
'usa-current text-primary': page.selected,
}
)}
className={classNames('usa-sidenav__item', styles.sideNav, {
'usa-current text-primary': page.selected,
})}
>
<a className={`${styles.usaNavLink}`} href={page.url}>
{page.title}
Expand Down
2 changes: 1 addition & 1 deletion packages/design/src/Form/components/PageSet/PageSet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const PageSet: PatternComponent<PageSetProps> = props => {
const { routeParams, pathname } = useRouteParams();
return (
<div className="grid-row">
<nav className="tablet:grid-col-3 tablet:padding-y-3 padding-right-4">
<nav className="tablet:grid-col-3 padding-x-2 tablet:padding-y-3 tablet:padding-right-4 tablet:padding-left-0">
<PageMenu
pages={props.pages.map((page, index) => {
const params = new URLSearchParams(routeParams?.toString());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import type { Meta, StoryObj } from '@storybook/react';

import { FormManagerProvider, useFormManagerStore } from '../../store';
import {
createTestFormManagerContext,
createTestSession,
createTwoPatternTestForm,
} from '../../../test-form';

import {
DraggableList,
DraggableListProps,
DraggableListPresentation,
} from './PreviewSequencePattern/DraggableList';
import { getPattern } from '@atj/forms';

const meta: Meta<typeof DraggableList> = {
title: 'patterns/DraggableList',
component: DraggableList,
decorators: [
(Story, args) => {
return (
<MemoryRouter initialEntries={['/']}>
<FormManagerProvider
context={createTestFormManagerContext()}
session={createTestSession({ form: createTwoPatternTestForm() })}
>
<Story {...args} />
</FormManagerProvider>
</MemoryRouter>
);
},
],
render: (args: DraggableListProps) => {
const store = useFormManagerStore();
const form = store.session.form;
const { updatePattern } = store;
const pattern = getPattern(form, 'root');

return (
<DraggableList
order={pattern.data.patterns}
presentation={args.presentation || 'default'}
updateOrder={order => {
updatePattern({
...pattern,
data: {
patterns: order,
},
});
}}
>
{pattern.data.patterns.map((item: string, index: number) => {
return (
<div key={index} className="padding-x-2 padding-y-1">
{item}
</div>
);
})}
</DraggableList>
);
},
tags: ['autodocs'],
};
export default meta;

export const Compact: StoryObj<typeof DraggableList> & {
args: { presentation: DraggableListPresentation };
} = {
args: {
presentation: 'compact',
},
};

export const Default: StoryObj<typeof DraggableList> & {
args: { presentation: DraggableListPresentation };
} = {
args: {
presentation: 'default',
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @vitest-environment jsdom
*/
import { describeStories } from '../../../test-helper';
import meta, * as stories from './DraggableList.stories';

describeStories(meta, stories);
Loading

0 comments on commit 6cda340

Please sign in to comment.