From a55bb9c4b2dda7dceee028afebfe1a9d3b632d16 Mon Sep 17 00:00:00 2001 From: Alf Eaton Date: Tue, 15 Aug 2023 15:23:35 +0100 Subject: [PATCH] [visual] Improve pasting of a table containing a caption (#14322) GitOrigin-RevId: 6d7676a9412c5614f85c17e16509c80ad937cc40 --- .../extensions/visual/paste-html.ts | 41 +++++++++++++++---- ...demirror-editor-visual-paste-html.spec.tsx | 17 ++++++++ 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/services/web/frontend/js/features/source-editor/extensions/visual/paste-html.ts b/services/web/frontend/js/features/source-editor/extensions/visual/paste-html.ts index 428e3b9dbf..126189cd83 100644 --- a/services/web/frontend/js/features/source-editor/extensions/visual/paste-html.ts +++ b/services/web/frontend/js/features/source-editor/extensions/visual/paste-html.ts @@ -30,7 +30,9 @@ export const pasteHtml = Prec.highest( ) return true - } catch { + } catch (error) { + console.error(error) + // fall back to the default paste handler return false } @@ -54,6 +56,9 @@ const htmlToLaTeX = (html: string) => { // remove style elements removeUnwantedElements(documentElement, 'style') + // pre-process table elements + processTables(documentElement) + // protect special characters in non-LaTeX text nodes protectSpecialCharacters(documentElement) @@ -147,6 +152,24 @@ const matchingParents = (element: HTMLElement, selector: string) => { return matches } +const processTables = (element: HTMLElement) => { + for (const table of element.querySelectorAll('table')) { + // create a wrapper element for the table and the caption + const container = document.createElement('div') + container.className = 'ol-table-wrap' + table.after(container) + + // move the caption (if it exists) into the container before the table + const caption = table.querySelector('caption') + if (caption) { + container.append(caption) + } + + // move the table into the container + container.append(table) + } +} + const tabular = (element: HTMLTableElement) => { const options = [] @@ -335,13 +358,15 @@ export const selectors = [ start: () => `\n\n\\begin{verbatim}\n`, end: () => `\n\\end{verbatim}\n\n`, }), + createSelector({ + selector: '.ol-table-wrap', + start: element => `\n\n\\begin{table}\n\\centering\n`, + end: () => `\n\\end{table}\n\n`, + }), createSelector({ selector: 'table', - start: element => - `\n\n\\begin{table}\n\\centering\n\\begin{tabular}{${tabular( - element - )}}\n`, - end: () => `\n\\end{tabular}\n\\end{table}\n\n`, + start: element => `\n\\begin{tabular}{${tabular(element)}}\n`, + end: () => `\n\\end{tabular}\n`, }), createSelector({ selector: 'thead', @@ -386,8 +411,8 @@ export const selectors = [ }, }), createSelector({ - selector: 'table > caption', - start: () => `\n\n\\caption{\\label{tab:example}`, + selector: 'caption', + start: () => `\n\n\\caption{`, end: () => `}\n\n`, }), createSelector({ diff --git a/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual-paste-html.spec.tsx b/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual-paste-html.spec.tsx index 4646104862..b64e8772ab 100644 --- a/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual-paste-html.spec.tsx +++ b/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual-paste-html.spec.tsx @@ -132,6 +132,23 @@ describe(' paste HTML in Visual mode', function () { ) }) + it('handles a pasted table with a caption', function () { + mountEditor() + + const data = + '
A table
foobar
' + + const clipboardData = new DataTransfer() + clipboardData.setData('text/html', data) + cy.spy(clipboardData, 'getData').as('get-data') + cy.get('@content').trigger('paste', { clipboardData }) + + cy.get('@content').should( + 'have.text', + 'A table\\begin{tabular}{c c}foo & bar ↩\\end{tabular}' + ) + }) + it('handles a pasted link', function () { mountEditor()