Skip to content

Commit 50cb860

Browse files
committed
Merge branch 'main' into feat/APP-4405
2 parents 67d0366 + e476181 commit 50cb860

6 files changed

Lines changed: 89 additions & 19 deletions

File tree

.changeset/fifty-bushes-tie.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

.changeset/seven-donkeys-stop.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# @aragon/gov-ui-kit
22

3+
## 1.12.0
4+
5+
### Minor Changes
6+
7+
- [#523](https://github.com/aragon/gov-ui-kit/pull/523) [`e153bc2`](https://github.com/aragon/gov-ui-kit/commit/e153bc2503ae1c985b2221453b122bacce735b53) Thanks [@dependabot](https://github.com/apps/dependabot)! - Update minor and patch NPM dependencies
8+
9+
- [#529](https://github.com/aragon/gov-ui-kit/pull/529) [`daeaccf`](https://github.com/aragon/gov-ui-kit/commit/daeaccf9928fc2180565fa60610984cb43a4c7bf) Thanks [@dependabot](https://github.com/apps/dependabot)! - Update minor and patch NPM dependencies
10+
11+
### Patch Changes
12+
13+
- [#524](https://github.com/aragon/gov-ui-kit/pull/524) [`74f4834`](https://github.com/aragon/gov-ui-kit/commit/74f483434137eb0b5401f4f89d6754d166d3a957) Thanks [@Fabricevladimir](https://github.com/Fabricevladimir)! - Add `valueFormat` prop in `TextAreaRichText` to control `onChange` output as `html`, `markdown` or `text`
14+
315
## 1.11.0
416

517
### Minor Changes

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@aragon/gov-ui-kit",
3-
"version": "1.11.0",
3+
"version": "1.12.0",
44
"description": "Implementation of the Aragon's Governance UI Kit",
55
"main": "dist/index.es.js",
66
"types": "dist/types/src/index.d.ts",

src/core/components/forms/textAreaRichText/textAreaRichText.test.tsx

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,34 @@ describe('<TextAreaRichText /> component', () => {
4444
expect(textbox.getAttribute('aria-labelledby')).toBeDefined();
4545
});
4646

47-
it('calls the onChange property on input change', async () => {
47+
it('calls the onChange property on input change with the value formatted as serialized HTML by default', async () => {
4848
const onChange = jest.fn();
4949
render(createTestComponent({ onChange }));
5050
await userEvent.type(screen.getByRole('textbox'), 'test');
5151
expect(onChange).toHaveBeenLastCalledWith('<p>test</p>');
5252
});
5353

54+
it("calls the onChange property on input change with plain text when 'valueFormat' is set to 'text'", async () => {
55+
const onChange = jest.fn();
56+
render(createTestComponent({ onChange, valueFormat: 'text' }));
57+
await userEvent.type(screen.getByRole('textbox'), 'test');
58+
expect(onChange).toHaveBeenLastCalledWith('test');
59+
});
60+
61+
it("calls the onChange property on input change with serialized HTML when 'valueFormat' is set to 'html'", async () => {
62+
const onChange = jest.fn();
63+
render(createTestComponent({ onChange, valueFormat: 'html' }));
64+
await userEvent.type(screen.getByRole('textbox'), 'test');
65+
expect(onChange).toHaveBeenLastCalledWith('<p>test</p>');
66+
});
67+
68+
it("calls the onChange property on input change with markdown when 'valueFormat' is set to 'markdown'", async () => {
69+
const onChange = jest.fn();
70+
render(createTestComponent({ onChange, valueFormat: 'markdown' }));
71+
await userEvent.type(screen.getByRole('textbox'), '# test');
72+
expect(onChange).toHaveBeenLastCalledWith('# test');
73+
});
74+
5475
it('defaults to empty string instead of empty paragraph when input is empty', async () => {
5576
const onChange = jest.fn();
5677
render(createTestComponent({ onChange }));
@@ -62,7 +83,9 @@ describe('<TextAreaRichText /> component', () => {
6283
it('formats pasted markdown content', async () => {
6384
const onChange = jest.fn();
6485
render(createTestComponent({ onChange }));
65-
const event = { getData: (type: string) => (type === 'text/plain' ? '# Heading' : '') } as DataTransfer;
86+
const event = {
87+
getData: (type: string) => (type === 'text/plain' ? '# Heading' : ''),
88+
} as DataTransfer;
6689
await userEvent.click(screen.getByRole('textbox'));
6790
await userEvent.paste(event);
6891
expect(onChange).toHaveBeenLastCalledWith('<h1>Heading</h1>');

src/core/components/forms/textAreaRichText/textAreaRichText.tsx

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import { StarterKit } from '@tiptap/starter-kit';
55
import classNames from 'classnames';
66
import { useEffect, useState } from 'react';
77
import { createPortal } from 'react-dom';
8-
import { Markdown } from 'tiptap-markdown';
8+
import { Markdown, type MarkdownStorage } from 'tiptap-markdown';
99
import { useRandomId } from '../../../hooks';
10-
import { InputContainer, type IInputContainerProps } from '../inputContainer';
10+
import { type IInputContainerProps, InputContainer } from '../inputContainer';
1111
import { TextAreaRichTextActions } from './textAreaRichTextActions';
1212

13+
export type ValueFormat = 'html' | 'markdown' | 'text';
14+
1315
export interface ITextAreaRichTextProps
1416
extends Omit<IInputContainerProps, 'maxLength' | 'inputLength' | 'value' | 'onChange' | 'id'> {
1517
/**
@@ -32,6 +34,13 @@ export interface ITextAreaRichTextProps
3234
* Whether to render the editor on the first render or not.
3335
*/
3436
immediatelyRender?: boolean;
37+
/**
38+
* Format of the input value, which determines how content is interpreted and returned.
39+
* Can be serialized HTML, markdown, or plain text.
40+
*
41+
* @default 'html'
42+
*/
43+
valueFormat?: ValueFormat;
3544
}
3645

3746
// Classes to properly style the TipTap placeholder
@@ -43,15 +52,29 @@ const placeholderClasses = classNames(
4352
);
4453

4554
export const TextAreaRichText: React.FC<ITextAreaRichTextProps> = (props) => {
46-
const { value, onChange, placeholder, disabled, className, id, immediatelyRender, ...containerProps } = props;
55+
const {
56+
value,
57+
onChange,
58+
placeholder,
59+
disabled,
60+
className,
61+
id,
62+
immediatelyRender,
63+
valueFormat = 'html',
64+
...containerProps
65+
} = props;
4766

4867
const [isExpanded, setIsExpanded] = useState(false);
4968

5069
const randomId = useRandomId(id);
5170

5271
const extensions = [
5372
StarterKit,
54-
Placeholder.configure({ placeholder, emptyNodeClass: placeholderClasses, showOnlyWhenEditable: false }),
73+
Placeholder.configure({
74+
placeholder,
75+
emptyNodeClass: placeholderClasses,
76+
showOnlyWhenEditable: false,
77+
}),
5578
Link,
5679
Markdown.configure({ transformPastedText: true }),
5780
];
@@ -69,7 +92,27 @@ export const TextAreaRichText: React.FC<ITextAreaRichTextProps> = (props) => {
6992
},
7093
},
7194
onUpdate: ({ editor }) => {
72-
const value = editor.getText() !== '' ? editor.getHTML() : '';
95+
if (editor.getText() === '') {
96+
onChange?.('');
97+
return;
98+
}
99+
100+
const handlers: Record<ValueFormat, () => string | undefined> = {
101+
html: () => editor.getHTML(),
102+
text: () => editor.getText(),
103+
markdown: () => {
104+
const markdownStorage = editor.storage.markdown as MarkdownStorage | undefined;
105+
106+
if (markdownStorage) {
107+
return markdownStorage.getMarkdown();
108+
}
109+
110+
return editor.getHTML();
111+
},
112+
};
113+
114+
const value = handlers[valueFormat]() ?? editor.getHTML();
115+
73116
onChange?.(value);
74117
},
75118
});
@@ -111,7 +154,9 @@ export const TextAreaRichText: React.FC<ITextAreaRichTextProps> = (props) => {
111154
'fixed top-0 left-0 z-[var(--guk-text-area-rich-text-expanded-z-index)] h-screen w-full [&>label]:hidden':
112155
isExpanded,
113156
})}
114-
wrapperClassName={classNames('grow overflow-hidden', { 'rounded-none!': isExpanded })}
157+
wrapperClassName={classNames('grow overflow-hidden', {
158+
'rounded-none!': isExpanded,
159+
})}
115160
id={randomId}
116161
{...containerProps}
117162
>

0 commit comments

Comments
 (0)