Skip to content

Commit 8ec5592

Browse files
feat: DP-159552 editor table support when pasting (#1034)
1 parent c8ec9fb commit 8ec5592

File tree

8 files changed

+164
-0
lines changed

8 files changed

+164
-0
lines changed

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
"@tiptap/extension-document": "2.12.0",
8787
"@tiptap/extension-floating-menu": "2.12.0",
8888
"@tiptap/extension-font-family": "2.12.0",
89+
"@tiptap/extension-gapcursor": "2.12.0",
8990
"@tiptap/extension-hard-break": "2.12.0",
9091
"@tiptap/extension-history": "2.12.0",
9192
"@tiptap/extension-image": "2.12.0",
@@ -97,6 +98,10 @@
9798
"@tiptap/extension-paragraph": "2.12.0",
9899
"@tiptap/extension-placeholder": "2.12.0",
99100
"@tiptap/extension-strike": "2.12.0",
101+
"@tiptap/extension-table": "2.12.0",
102+
"@tiptap/extension-table-cell": "2.12.0",
103+
"@tiptap/extension-table-header": "2.12.0",
104+
"@tiptap/extension-table-row": "2.12.0",
100105
"@tiptap/extension-text": "2.12.0",
101106
"@tiptap/extension-text-align": "2.12.0",
102107
"@tiptap/extension-text-style": "2.12.0",

packages/dialtone-css/lib/build/less/components/rich-text-editor.less

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@
4242
padding-left: var(--dt-space-400);
4343
border-left: var(--dt-size-border-300) solid var(--dt-color-border-subtle);
4444
}
45+
46+
table {
47+
border-collapse: collapse;
48+
}
49+
50+
td {
51+
padding-left: var(--dt-space-200);
52+
border: var(--dt-size-border-100) solid var(--dt-color-foreground-primary);
53+
}
4554
}
4655
}
4756

packages/dialtone-vue/components/rich_text_editor/rich_text_editor.test.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,64 @@ describe('DtRichTextEditor tests', () => {
471471
output = wrapper.vm.getOutput();
472472
expect(output).toBe('<strong>bold pasted text</strong>');
473473
});
474+
it('should preserve tables when pasted', async () => {
475+
// When new extensions are added due to change in props editor needs to be recreated
476+
wrapper.unmount();
477+
props['outputFormat'] = 'html';
478+
props['modelValue'] = '';
479+
props['allowTables'] = true;
480+
editorEl?.remove();
481+
wrapper = mount(DtRichTextEditor, {
482+
props,
483+
components: { EditorContent },
484+
listeners,
485+
attrs,
486+
slots,
487+
attachTo: document.body,
488+
});
489+
await wrapper.vm.$nextTick();
490+
editor = wrapper.find('[data-qa="dt-rich-text-editor"]').find('div[contenteditable]');
491+
editorEl = document.getElementsByClassName('qa-editor')[0];
492+
493+
const clipboardData = new DataTransfer();
494+
clipboardData.setData('text/plain', 'testtesttesttest');
495+
clipboardData.setData('text/html', '<table><tr><td>test</td><td>test</td></tr><tr><td>test</td><td>test</td></tr></table>');
474496

497+
const pasteEvent = new ClipboardEvent('paste', {
498+
clipboardData,
499+
bubbles: true,
500+
cancelable: true,
501+
});
502+
503+
editorEl.dispatchEvent(pasteEvent);
504+
await wrapper.vm.$nextTick();
505+
const output = wrapper.vm.getOutput();
506+
// Check that the table has been converted to a table with the enforced styling
507+
expect(output).toBe(`<table style="min-width: 50px;"><colgroup><col style="min-width: 25px;"><col style="min-width: 25px;"></colgroup><tbody><tr><td colspan="1" rowspan="1"><p>test</p></td><td colspan="1" rowspan="1"><p>test</p></td></tr><tr><td colspan="1" rowspan="1"><p>test</p></td><td colspan="1" rowspan="1"><p>test</p></td></tr></tbody></table>`);
508+
});
509+
510+
it('should not preserve tables when pasted without allow tables', async () => {
511+
await wrapper.setProps({
512+
pasteRichText: false,
513+
outputFormat: 'html',
514+
modelValue: '',
515+
});
516+
const clipboardData = new DataTransfer();
517+
clipboardData.setData('text/plain', 'testtesttesttest');
518+
clipboardData.setData('text/html', '<table><tr><td>test</td><td>test</td></tr><tr><td>test</td><td>test</td></tr></table>');
519+
520+
const pasteEvent = new ClipboardEvent('paste', {
521+
clipboardData,
522+
bubbles: true,
523+
cancelable: true,
524+
});
525+
526+
editorEl.dispatchEvent(pasteEvent);
527+
await wrapper.vm.$nextTick();
528+
const output = wrapper.vm.getOutput();
529+
// Check that the table has been converted to text;
530+
expect(output).toBe(`<p>testtesttesttest</p>`);
531+
});
475532
it('if pasteRichText is false, line breaks should be preserved as hard breaks', async () => {
476533
await wrapper.setProps({
477534
pasteRichText: false,

packages/dialtone-vue/components/rich_text_editor/rich_text_editor.vue

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import Blockquote from '@tiptap/extension-blockquote';
6161
import CodeBlock from '@tiptap/extension-code-block';
6262
import Code from '@tiptap/extension-code';
6363
import Document from '@tiptap/extension-document';
64+
import Gapcursor from '@tiptap/extension-gapcursor';
6465
import HardBreak from '@tiptap/extension-hard-break';
6566
import Paragraph from '@tiptap/extension-paragraph';
6667
import Placeholder from '@tiptap/extension-placeholder';
@@ -75,6 +76,10 @@ import Underline from '@tiptap/extension-underline';
7576
import Text from '@tiptap/extension-text';
7677
import TextAlign from '@tiptap/extension-text-align';
7778
import History from '@tiptap/extension-history';
79+
import Table from '@tiptap/extension-table';
80+
import TableCell from '@tiptap/extension-table-cell';
81+
import TableHeader from '@tiptap/extension-table-header';
82+
import TableRow from '@tiptap/extension-table-row';
7883
import TextStyle from '@tiptap/extension-text-style';
7984
import Color from '@tiptap/extension-color';
8085
import FontFamily from '@tiptap/extension-font-family';
@@ -406,6 +411,14 @@ export default {
406411
type: Boolean,
407412
default: false,
408413
},
414+
415+
/**
416+
* Allow Tables to be used in to the editor
417+
*/
418+
allowTables: {
419+
type: Boolean,
420+
default: false,
421+
},
409422
},
410423
411424
emits: [
@@ -839,6 +852,10 @@ export default {
839852
extensions.push(...this.additionalExtensions);
840853
}
841854
855+
if (this.allowTables) {
856+
extensions.push(Table.configure({ resizable: true }), TableRow, TableHeader, TableCell, Gapcursor)
857+
}
858+
842859
return extensions;
843860
},
844861
@@ -1251,6 +1268,7 @@ export default {
12511268
focusEditor () {
12521269
this.editor.commands.focus();
12531270
},
1271+
12541272
},
12551273
};
12561274
</script>

packages/dialtone-vue/components/rich_text_editor/rich_text_editor_default.story.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
:additional-extensions="$attrs.additionalExtensions"
2626
:hide-link-bubble-menu="$attrs.hideLinkBubbleMenu"
2727
:use-div-tags="$attrs.useDivTags"
28+
:allow-tables="$attrs.allowTables"
2829
@blur="$attrs.onBlur"
2930
@input="$attrs.onInput"
3031
@edit-link="$attrs.onEditLink"

packages/dialtone-vue/recipes/conversation_view/editor/editor.vue

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@
175175
:output-format="htmlOutputFormat"
176176
:placeholder="placeholder"
177177
:use-div-tags="useDivTags"
178+
:allow-tables="allowTables"
178179
data-qa="dt-rich-text-editor"
179180
v-bind="removeClassStyleAttrs($attrs)"
180181
@text-input="onTextInput"
@@ -467,6 +468,14 @@ export default {
467468
type: Boolean,
468469
default: false,
469470
},
471+
472+
/**
473+
* Allow Tables to be used in to the editor
474+
*/
475+
allowTables: {
476+
type: Boolean,
477+
default: false,
478+
},
470479
},
471480
472481
emits: [

packages/dialtone-vue/recipes/conversation_view/editor/editor_default.story.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
:show-quick-replies-button="$attrs.showQuickRepliesButton"
2828
:show-code-block-button="$attrs.showCodeBlockButton"
2929
:show-inline-image-button="$attrs.showInlineImageButton"
30+
:allow-tables="$attrs.allowTables"
3031
@focus="$attrs.onFocus"
3132
@blur="$attrs.onBlur"
3233
@input="$attrs.onInput"

pnpm-lock.yaml

Lines changed: 64 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)