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

Add Rich text editor #294

Merged
merged 70 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from 67 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
478c5e1
copy all files from paragraph to use as the basis for the rich text c…
ethangardner Aug 1, 2024
f033ead
update icons for rich text component
ethangardner Aug 1, 2024
549e3b2
resolve conflicts
ethangardner Aug 13, 2024
868346e
wip on text formatting
ethangardner Aug 7, 2024
e5547c3
get ready for setvalue
ethangardner Aug 8, 2024
a6f847d
Refactor rich text handling in form components
ethangardner Aug 9, 2024
7945299
code formatting
ethangardner Aug 9, 2024
8e4e08b
add custom toolbar modules
ethangardner Aug 12, 2024
b0b1876
formatting
ethangardner Aug 12, 2024
9abadc0
add types to handlechange
ethangardner Aug 12, 2024
8a53ce6
add small format
ethangardner Aug 13, 2024
b7052e7
pare down editor features
ethangardner Aug 13, 2024
7d902eb
hidden field to mirror editor contents
ethangardner Aug 13, 2024
475fe1a
install react-quill and increaes concurrency
ethangardner Aug 14, 2024
badb156
fix type
ethangardner Aug 14, 2024
3a91a54
remove logging
ethangardner Aug 14, 2024
d970b9b
remove logging
ethangardner Aug 14, 2024
8512f27
remove logging
ethangardner Aug 14, 2024
855574c
allow br tag in editor
ethangardner Aug 14, 2024
e9ea44f
formatting and label updates
ethangardner Aug 14, 2024
d2b3054
swap quill for tiptap
ethangardner Aug 14, 2024
faffa80
fix build error with failing checks in form package
ethangardner Aug 14, 2024
e2fddd2
Rename and update RichTextPatternEdit components
ethangardner Aug 15, 2024
f4a1121
Refactor editor button actions into a reusable array
ethangardner Aug 15, 2024
f834eac
Add tests for lists
ethangardner Aug 16, 2024
042adb2
Dry out test a little
ethangardner Aug 16, 2024
448756f
dry out test even more.
ethangardner Aug 16, 2024
9de84fb
code formatting
ethangardner Aug 16, 2024
4461d29
Add CSS styles for RichText components
ethangardner Aug 16, 2024
679a90c
Remove react-quill
ethangardner Aug 16, 2024
62fc127
remove prop from type for rich text
ethangardner Aug 16, 2024
175e71a
use patternvalue instead of string to correct typing issue
ethangardner Aug 16, 2024
b3e6a1f
update styles of texteditor
ethangardner Aug 16, 2024
b1fde84
exclude test that relies on browser feature from vitest runner
ethangardner Aug 16, 2024
36dfa6b
Merge branch 'main' into 279-stub-text-formatting
ethangardner Aug 20, 2024
df3dd99
cleanup vitest config
ethangardner Aug 20, 2024
ebba91b
remove save on blur for rich-text fields
ethangardner Aug 20, 2024
422ee2c
change button type
ethangardner Aug 20, 2024
ca8bb6c
remove top margin from editor content first child
ethangardner Aug 20, 2024
b56ca47
add focus to editor when initialized
ethangardner Aug 20, 2024
36d15c2
fix css selector
ethangardner Aug 20, 2024
7114045
remove richtext check onblur
ethangardner Aug 20, 2024
9e2f977
remove dbs from git
ethangardner Aug 20, 2024
2311e59
Merge branch 'main' into 279-stub-text-formatting
ethangardner Aug 22, 2024
63c20ff
update form handling submission
ethangardner Aug 22, 2024
24f2be1
Add debounce for improved performance in RichTextPatternEdit
ethangardner Aug 22, 2024
3415805
Add optional validation rule to form fields
ethangardner Aug 22, 2024
909013f
Add optional validation rule to form fields
ethangardner Aug 22, 2024
7128447
add adr
ethangardner Aug 22, 2024
69839f6
Merge branch 'main' into 279-stub-text-formatting
ethangardner Aug 22, 2024
2c8dee9
Refactor MenuBar for enhanced editor functionality
ethangardner Aug 22, 2024
d455a14
add types and style editor for long blocks of text.
ethangardner Aug 23, 2024
f2fd5a2
turn off spellcheck. squigglies were problematic for long documents.
ethangardner Aug 23, 2024
71c8f72
match styles from edit view to preview.
ethangardner Aug 23, 2024
9b07a4a
move label
ethangardner Aug 23, 2024
8ed8b97
use vitest browser and install deps
ethangardner Aug 19, 2024
566d089
update test config to try running browser tests in CI
ethangardner Aug 29, 2024
17cd931
install playwright browsers in ci
ethangardner Aug 29, 2024
2bd06ed
install dependencies
ethangardner Aug 29, 2024
b435a66
update install command
ethangardner Aug 29, 2024
5838d2f
fix failing tests in e2e
ethangardner Aug 29, 2024
697c29c
formatting code
ethangardner Aug 29, 2024
a8ebacd
update test commands for watch and dev scenarios
ethangardner Aug 30, 2024
f030169
update documentation
ethangardner Aug 30, 2024
a6e636c
add aria-live for contents that change with soft nav
ethangardner Aug 30, 2024
34057c4
added css file extensions to prettier config
ethangardner Aug 30, 2024
fe262e6
Merge remote-tracking branch 'origin/main' into 279-stub-text-formatting
danielnaab Sep 3, 2024
e7387d3
update deprecated command
ethangardner Sep 4, 2024
bba6dfd
Rich Text Component Toolbar buttons & API updates (#300)
jimmoffet Sep 4, 2024
265542b
Merge branch 'main' into 279-stub-text-formatting
ethangardner Sep 5, 2024
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
3 changes: 3 additions & 0 deletions .github/workflows/_validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ jobs:
- name: Install dependencies
run: pnpm install

- name: Install playwright
run: pnpx playwright install --with-deps
ethangardner marked this conversation as resolved.
Show resolved Hide resolved

# While most of the test suite is self-contained, the tests for the demo
# servers require a prod build of @atj/server.
- name: Build
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ packages/form-service
/e2e/playwright-report/
/e2e/blob-report/
/e2e/playwright/.cache/
__screenshots__/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ This project uses [pnpm workspaces](https://pnpm.io/workspaces). To work with th
pnpm install
```

To run the complete test suite, with coverage metrics generated:
To install the browsers needed for the Storybook testing with `@vitest/browser`, you need to do a one-time install with `pnpx install playwright --with-deps`. To run the complete test suite, with coverage metrics generated:

```bash
pnpm test
Expand Down
1 change: 1 addition & 0 deletions apps/server-doj/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
dist/
*.db
1 change: 1 addition & 0 deletions apps/server-kansas/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
dist/
*.db
21 changes: 21 additions & 0 deletions documents/adr/0012-rich-text-editor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 12. Rich Text Editor

Date: 2024-08-12

## Status

Approved

## Context

In user testing, there was an expressed interest to be able to apply a limited set of rich text formatting to imported forms and new builds from scratch.

## Decision

TipTap was selected to provide the rich text formatting features after evaluating several alternatives (QuillJS, Prose, TinyMCE).

Quill was originally installed, but there was a known issue in being able to execute a line break inside a bulleted list, which caused an issue with reformatting imported text when it was edited for the first time. TipTap was installed as an alternative.

## Consequences

TipTap is a popular package that works outside the React ecosystem, so it is portable if there is ever a need to move to a different JS framework. The plugin does operate on a freemium model with the subset of features being free with additional, more advanced features having a cost. However, based on the features we use at the time of this ADR, the free features are robust enough to current requirements as well as the most likely requirements in the future.
8 changes: 4 additions & 4 deletions e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,22 @@ First, make sure the script is executable:

```bash
# from the project root
chmod +x ./e2e/scripts/end-to-end.sh
chmod +x ./e2e/script/end-to-end.sh
```

Examples:

```bash
# builds the test container (also will run the tests)
./e2e/scripts/end-to-end.sh -f build_container -t test
./e2e/script/end-to-end.sh -f build_container -t test
```

```bash
# builds the serve container and start it
./e2e/scripts/end-to-end.sh -f build_container -t serve -f run_container
./e2e/script/end-to-end.sh -f build_container -t serve -f run_container
```

```bash
# Runs the default tasks `end_to_end` and `interaction`
./e2e/scripts/end-to-end.sh
./e2e/script/end-to-end.sh
```
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
"scripts": {
"build": "turbo run build --filter=!@atj/infra-cdktf",
"clean": "turbo run clean",
"dev": "turbo run dev --concurrency 18",
"format": "prettier --write \"packages/*/src/**/*.{js,jsx,ts,tsx,scss}\"",
"dev": "turbo run dev --concurrency 20",
"format": "prettier --write \"packages/*/src/**/*.{js,jsx,ts,tsx,scss,css}\"",
"lint": "turbo run lint",
"pages": "rm -rf node_modules && npm i -g pnpm turbo && pnpm i && pnpm build && ln -sf ./apps/spotlight/dist _site",
"test": "vitest run",
"test:ci": "vitest run # --coverage.enabled --coverage.provider=v8 --coverage.reporter=text --coverage.reporter=json-summary --coverage.reporter=json --coverage.reportOnFailure",
"test": "vitest run && pnpm --filter @atj/design test:ci",
"test:ci": "vitest run && pnpm --filter @atj/design test:ci # --coverage.enabled --coverage.provider=v8 --coverage.reporter=text --coverage.reporter=json-summary --coverage.reporter=json --coverage.reportOnFailure",
"test:infra": "turbo run --filter=infra-cdktf test",
"typecheck": "tsc --build",
"prepare": "husky"
Expand Down
5 changes: 5 additions & 0 deletions packages/common/src/locales/en/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ export const en = {
displayName: 'Paragraph',
errorTextMustContainChar: 'String must contain at least 1 character(s)',
},
richText: {
fieldLabel: 'Rich text',
displayName: 'Rich text',
errorTextMustContainChar: 'String must contain at least 1 character(s)',
},
radioGroup: {
...defaults,
displayName: 'Radio group label',
Expand Down
2 changes: 2 additions & 0 deletions packages/design/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ See relevant ADRs:

- [documents/adr/0007-initial-css-strategy](../../documents/adr/0007-initial-css-strategy.md)
- [documents/adr/0009-design-assets-workflow.md](../../documents/adr/0009-design-assets-workflow.md)

This package as a special watch task. If your dev server is running already (`pnpm dev`), you can open a separate terminal and run `pnpm test:watch` and any changes to the *.{ts,tsx} files in this package will run the test suite. If you'd like to run from the project root directory, you would run `pnpm --filter @atj/design test:watch`.
12 changes: 11 additions & 1 deletion packages/design/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
"dev:styles": "gulp watch",
"lint": "eslint --ext ts,tsx --report-unused-disable-directives --max-warnings 0 src",
"test:url": "test-storybook --url http://127.0.0.1:9009 --config-dir .storybook",
"test:ci": "concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"pnpm build:storybook --quiet && pnpm http-server storybook-static --port 9009 --silent\" \"wait-on tcp:127.0.0.1:9009 && pnpm test:url --maxWorkers=2\""
"test:dev": "wait-on tcp:127.0.0.1:9009 && pnpm test:url --maxWorkers=2 # assumes dev server is running",
"test:ci": "concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"pnpm build:storybook --quiet && pnpm http-server storybook-static --port 9009 --silent\" \"pnpm test:dev\"",
"test:watch": "pnpm onchange './**/*.{tsx,ts}' -- pnpm test:url"
},
"files": [
"dist/**/*"
Expand Down Expand Up @@ -45,17 +47,21 @@
"@typescript-eslint/parser": "^7.18.0",
"@uswds/compile": "1.1.0",
"@vitejs/plugin-react": "^4.3.1",
"@vitest/browser": "^2.0.5",
"concurrently": "^8.2.2",
"eslint": "^8.57.0",
"eslint-plugin-react": "^7.35.0",
"gulp": "^5.0.0",
"http-server": "^14.1.1",
"install": "^0.13.0",
"jsdom": "^24.1.1",
"onchange": "^7.1.0",
"playwright": "^1.46.1",
"prop-types": "^15.8.1",
"react-dom": "^18.3.1",
"vite": "^5.4.0",
"vite-plugin-dts": "^4.0.1",
"vitest": "^2.0.5",
"wait-on": "^7.2.0"
},
"dependencies": {
Expand All @@ -64,8 +70,12 @@
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@tiptap/core": "^2.6.2",
"@tiptap/react": "^2.6.2",
"@tiptap/starter-kit": "^2.6.2",
"@uswds/uswds": "^3.8.1",
"classnames": "^2.5.1",
"debounce": "^2.1.0",
"deep-equal": "^2.2.3",
"react": "^18.3.1",
"react-hook-form": "^7.52.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
.sideNavWrapper {
top: 6rem;
top: 6rem;
}

.sideNav a:not(.usa-button):not(.usa-current) {
color: #005ea2;
color: #005ea2;
}

.sideNav a:not(.usa-button):hover {
background-color: #d9e8f6;
background-color: #d9e8f6;
}
5 changes: 4 additions & 1 deletion packages/design/src/Form/components/PageSet/PageSet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ const PageSet: PatternComponent<PageSetProps> = props => {
})}
/>
</nav>
<div className="tablet:grid-col-9 tablet:padding-left-4 padding-left-0 padding-bottom-3 padding-top-3 tablet:border-left tablet:border-base-lighter contentWrapper">
<div
className="tablet:grid-col-9 tablet:padding-left-4 padding-left-0 padding-bottom-3 padding-top-3 tablet:border-left tablet:border-base-lighter contentWrapper"
aria-live="polite"
>
{props.children}
<ActionBar actions={props.actions} />
</div>
Expand Down
14 changes: 14 additions & 0 deletions packages/design/src/Form/components/RichText/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';

import { type RichTextProps } from '@atj/forms';
import { type PatternComponent } from '../../../Form/index.js';
import styles from './richTextStyles.module.css';

const FormSummary: PatternComponent<RichTextProps> = props => {
return (
<div className={`${styles.richTextEditorWrapper}`}>
<div dangerouslySetInnerHTML={{ __html: props.text }} />
</div>
);
};
export default FormSummary;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.richTextEditorWrapper li > p {
margin-top: 0;
margin-bottom: 0;
padding-top: 0;
padding-bottom: 0;
}
2 changes: 2 additions & 0 deletions packages/design/src/Form/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Page from './Page/index.js';
import PageSet from './PageSet/index.js';
import Paragraph from './Paragraph/index.js';
import RadioGroup from './RadioGroup/index.js';
import RichText from './RichText/index.js';
import Sequence from './Sequence/index.js';
import SubmissionConfirmation from './SubmissionConfirmation/index.js';
import TextInput from './TextInput/index.js';
Expand All @@ -22,6 +23,7 @@ export const defaultPatternComponents: ComponentForPattern = {
'page-set': PageSet as PatternComponent,
paragraph: Paragraph as PatternComponent,
'radio-group': RadioGroup as PatternComponent,
'rich-text': RichText as PatternComponent,
sequence: Sequence as PatternComponent,
'submission-confirmation': SubmissionConfirmation as PatternComponent,
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import type { Meta, StoryObj } from '@storybook/react';

import DocumentImporter from '.';
import { createTwoPatternTestForm } from '../../../test-form';
import DocumentImporter from './index.js';
import { createTwoPatternTestForm } from '../../../test-form.js';

const meta: Meta<typeof DocumentImporter> = {
title: 'FormManager/DocumentImporter',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* @vitest-environment jsdom
*/
import { describeStories } from '../../../test-helper';
import meta, * as stories from './DocumentImporter.stories';
import { describeStories } from '../../../test-helper.js';
import meta, * as stories from './DocumentImporter.stories.js';

describeStories(meta, stories);
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import checkboxIcon from './images/checkbox-icon.svg';
import dateIcon from './images/date-icon.svg';
import dropDownIcon from './images/dropdown-icon.svg';
import dropDownOptionIcon from './images/dropdownoption-icon.svg';
import richTextIcon from './images/richtext-icon.svg';
import longanswerIcon from './images/longanswer-icon.svg';
import pageIcon from './images/page-icon.svg';
import shortanswerIcon from './images/shortanswer-icon.svg';
Expand All @@ -24,6 +25,7 @@ const icons: Record<string, string | any> = {
'date-icon.svg.svg': dateIcon,
'dropdown-icon.svg': dropDownIcon,
'dropdownoption-icon.svg': dropDownOptionIcon,
'richtext-icon.svg': richTextIcon,
'longanswer-icon.svg': longanswerIcon,
'page-icon.svg': pageIcon,
'shortanswer-icon.svg': shortanswerIcon,
Expand Down Expand Up @@ -89,12 +91,14 @@ const sidebarPatterns: DropdownPattern[] = [
['fieldset', defaultFormConfig.patterns['fieldset']],
['input', defaultFormConfig.patterns['input']],
['paragraph', defaultFormConfig.patterns['paragraph']],
['rich-text', defaultFormConfig.patterns['rich-text']],
['radio-group', defaultFormConfig.patterns['radio-group']],
] as const;
export const fieldsetPatterns: DropdownPattern[] = [
['form-summary', defaultFormConfig.patterns['form-summary']],
['input', defaultFormConfig.patterns['input']],
['paragraph', defaultFormConfig.patterns['paragraph']],
['rich-text', defaultFormConfig.patterns['rich-text']],
['radio-group', defaultFormConfig.patterns['radio-group']],
] as const;

Expand Down
20 changes: 13 additions & 7 deletions packages/design/src/FormManager/FormEdit/FormEdit.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,14 @@ export const FormEditAddPattern: StoryObj<typeof FormEdit> = {
await userEvent.click(canvas.getByText('Pattern 1'));
//await userEvent.selectOptions(select, 'Text input');

select.forEach(async element => {
await userEvent.click(element);
});
await Promise.all(
select.map(async element => {
return await userEvent.click(element);
})
);

const finalCount = (await canvas.findAllByRole('textbox')).length;
expect(finalCount).toBeGreaterThan(initialCount);
await expect(finalCount).toBeGreaterThan(initialCount);
},
};

Expand All @@ -82,9 +84,13 @@ const editFieldLabel = async (
await userEvent.type(input, updatedLabel);
//await userEvent.click(canvas.getByText('Add Element'));

select.forEach(async element => {
await userEvent.click(element);
});
await Promise.all(
select.map(async element => {
return await userEvent.click(element);
})
);

await userEvent.click(canvas.getByText(/save and close/i));

waitFor(
async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ const PageSetEdit: PatternEditComponent<PageSetProps> = ({ previewProps }) => {
})}
/>
</nav>
<div className="tablet:grid-col-9 tablet:padding-left-4 padding-left-0 padding-bottom-3 padding-top-3 tablet:border-left tablet:border-base-lighter contentWrapper">
<div
className="tablet:grid-col-9 tablet:padding-left-4 padding-left-0 padding-bottom-3 padding-top-3 tablet:border-left tablet:border-base-lighter contentWrapper"
aria-live="polite"
>
{previewProps.children}
<ActionBar actions={previewProps.actions} />
</div>
Expand Down
Loading
Loading