Update text transfer to use line breaks

This commit is contained in:
Nate Kelley 2025-10-01 19:33:10 -06:00
parent dd2cab04b8
commit da329ee408
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
2 changed files with 133 additions and 20 deletions

View File

@ -0,0 +1,106 @@
import type { Editor } from '@tiptap/react';
import { describe, expect, it, vi } from 'vitest';
import type { MentionSuggestionExtension } from './MentionInput.types';
import type { MentionPillAttributes } from './MentionPill';
import { onUpdateTransformer } from './update-transformers';
describe('onUpdateTransformer', () => {
it('should preserve newlines between paragraphs', () => {
const mockEditor = {
getText: vi.fn().mockReturnValue('Hello\nWorld'),
getJSON: vi.fn().mockReturnValue({
type: 'doc',
content: [
{
type: 'paragraph',
content: [
{
type: 'text',
text: 'Hello',
},
],
},
{
type: 'paragraph',
content: [
{
type: 'text',
text: 'World',
},
],
},
],
}),
} as unknown as Editor;
const result = onUpdateTransformer({
editor: mockEditor,
mentionsByTrigger: {},
});
expect(result.transformedValue).toBe('Hello\nWorld');
expect(result.arrayValue).toEqual([
{ type: 'text', text: 'Hello' },
{ type: 'text', text: '\n' },
{ type: 'text', text: 'World' },
]);
expect(result.editorText).toBe('Hello\nWorld');
});
it('should handle mentions with custom transform in multi-paragraph content', () => {
const mockEditor = {
getText: vi.fn().mockReturnValue('Hello @user\nWorld'),
getJSON: vi.fn().mockReturnValue({
type: 'doc',
content: [
{
type: 'paragraph',
content: [
{
type: 'text',
text: 'Hello ',
},
{
type: 'mention',
attrs: {
id: 'user123',
label: '@user',
trigger: '@',
value: 'user123',
},
},
],
},
{
type: 'paragraph',
content: [
{
type: 'text',
text: 'World',
},
],
},
],
}),
} as unknown as Editor;
const mentionsByTrigger: Record<string, MentionSuggestionExtension> = {
'@': {
onChangeTransform: (attrs: MentionPillAttributes) => `@[${attrs.value}]`,
} as MentionSuggestionExtension,
};
const result = onUpdateTransformer({
editor: mockEditor,
mentionsByTrigger,
});
expect(result.transformedValue).toBe('Hello @[user123]\nWorld');
expect(result.arrayValue).toEqual([
{ type: 'text', text: 'Hello ' },
{ type: 'mention', attrs: { id: 'user123', label: '@user', trigger: '@', value: 'user123' } },
{ type: 'text', text: '\n' },
{ type: 'text', text: 'World' },
]);
});
});

View File

@ -16,34 +16,41 @@ export const onUpdateTransformer = ({
}): MentionOnChange => {
const editorText = editor.getText();
const editorJson = editor.getJSON();
const arrayValue: MentionArrayItem[] = editorJson.content.reduce<MentionArrayItem[]>(
(acc, item) => {
if (item.type === 'paragraph') {
item.content?.forEach((item) => {
let transformedValue = '';
const arrayValue: MentionArrayItem[] = [];
editorJson.content.forEach((paragraph, paragraphIndex) => {
if (paragraph.type === 'paragraph') {
// Handle paragraph content (text and mentions)
if (paragraph.content && paragraph.content.length > 0) {
paragraph.content.forEach((item) => {
if (item.type === 'text') {
const _item = item as TextType;
acc.push({ type: 'text', text: _item.text });
arrayValue.push({ type: 'text', text: _item.text });
transformedValue += _item.text;
} else if (item.type === 'mention') {
const _item = item as NodeType<'mention', MentionPillAttributes>;
acc.push({ type: 'mention', attrs: _item.attrs });
arrayValue.push({ type: 'mention', attrs: _item.attrs });
const onChangeTransform = mentionsByTrigger[_item.attrs.trigger]?.onChangeTransform;
if (onChangeTransform) {
transformedValue += onChangeTransform(_item.attrs);
} else {
transformedValue += _item.attrs.label;
}
}
});
}
return acc;
},
[]
);
const transformedValue = arrayValue.reduce((acc, item) => {
if (item.type === 'text') {
return acc + item.text;
// Add double newline after each paragraph to match TipTap's getText() behavior
// TipTap adds \n\n between paragraphs (visible as single blank line)
if (paragraphIndex < editorJson.content.length - 1) {
arrayValue.push({ type: 'text', text: '\n' });
transformedValue += '\n';
}
}
if (item.type === 'mention') {
const onChangeTransform = mentionsByTrigger[item.attrs.trigger]?.onChangeTransform;
if (onChangeTransform) return acc + onChangeTransform(item.attrs);
return acc + item.attrs.label;
}
return acc;
}, '');
});
return {
transformedValue,