From a5867074e5fd232c6f3c527b5759c46e1635ebe8 Mon Sep 17 00:00:00 2001 From: M-ZubairAhmed Date: Mon, 26 Jun 2023 17:47:04 +0530 Subject: [PATCH] MM-53006 : Using the "link" button puts the URL after [url] instead of replacing [url] when pasting (#23784) --- .../src/utils/markdown/apply_markdown.ts | 4 +- webapp/channels/src/utils/paste.test.tsx | 27 ++++++++++ webapp/channels/src/utils/paste.tsx | 50 +++++++++++++------ 3 files changed, 64 insertions(+), 17 deletions(-) diff --git a/webapp/channels/src/utils/markdown/apply_markdown.ts b/webapp/channels/src/utils/markdown/apply_markdown.ts index 8139fa270c..09c89465e8 100644 --- a/webapp/channels/src/utils/markdown/apply_markdown.ts +++ b/webapp/channels/src/utils/markdown/apply_markdown.ts @@ -401,7 +401,9 @@ function applyBoldItalicMarkdown({selectionEnd, selectionStart, message, markdow }; } -export function applyLinkMarkdown({selectionEnd, selectionStart, message, url = 'url'}: ApplyLinkMarkdownOptions) { +export const DEFAULT_PLACEHOLDER_URL = 'url'; + +export function applyLinkMarkdown({selectionEnd, selectionStart, message, url = DEFAULT_PLACEHOLDER_URL}: ApplyLinkMarkdownOptions) { // const prefix = message.slice(0, selectionStart); const selection = message.slice(selectionStart, selectionEnd); diff --git a/webapp/channels/src/utils/paste.test.tsx b/webapp/channels/src/utils/paste.test.tsx index 13a6370b1a..253808e364 100644 --- a/webapp/channels/src/utils/paste.test.tsx +++ b/webapp/channels/src/utils/paste.test.tsx @@ -170,6 +170,33 @@ describe('formatMarkdownLinkMessage', () => { const formatttedMarkdownLinkMessage = formatMarkdownLinkMessage({selectionStart: 0, selectionEnd: 4, message, clipboardData}); expect(formatttedMarkdownLinkMessage).toEqual('[test](https://example.com/)'); }); + + test('Should not add link when pasting inside of a formatted markdown link', () => { + const message = '[test](url)'; + const formatttedMarkdownLinkMessage = formatMarkdownLinkMessage({selectionStart: 7, selectionEnd: 10, message, clipboardData}); + expect(formatttedMarkdownLinkMessage).toEqual('https://example.com/'); + }); + + test('Should add link when pasting inside of an improper formatted markdown link', () => { + const improperFormattedLinkMessages = [ + {message: '[test](url)', selection: 'ur', expected: '[ur](https://example.com/)'}, + {message: '[test](url)', selection: '(url', expected: '[(url](https://example.com/)'}, + {message: '[test](url)', selection: 'url)', expected: '[url)](https://example.com/)'}, + {message: '[test](url)', selection: '(url)', expected: '[(url)](https://example.com/)'}, + {message: '[test](url)', selection: '[test](url', expected: '[[test](url](https://example.com/)'}, + {message: '[test](url)', selection: 'test](url', expected: '[test](url](https://example.com/)'}, + {message: '[test](url)', selection: 'test](url)', expected: '[test](url)](https://example.com/)'}, + {message: '[test](url)', selection: '[test](url)', expected: '[[test](url)](https://example.com/)'}, + ]; + + for (const {message, selection, expected} of improperFormattedLinkMessages) { + const selectionStart = message.indexOf(selection); + const selectionEnd = selectionStart + selection.length; + + const formatttedMarkdownLinkMessage = formatMarkdownLinkMessage({selectionStart, selectionEnd, message, clipboardData}); + expect(formatttedMarkdownLinkMessage).toEqual(expected); + } + }); }); describe('isTextUrl', () => { diff --git a/webapp/channels/src/utils/paste.tsx b/webapp/channels/src/utils/paste.tsx index dc415f2ea2..b98b5c8fc0 100644 --- a/webapp/channels/src/utils/paste.tsx +++ b/webapp/channels/src/utils/paste.tsx @@ -3,13 +3,7 @@ import turndownService from 'utils/turndown'; import {splitMessageBasedOnCaretPosition, splitMessageBasedOnTextSelection} from 'utils/post_utils'; - -type FormatMarkdownParams = { - message: string; - clipboardData: DataTransfer; - selectionStart: number | null; - selectionEnd: number | null; -}; +import {DEFAULT_PLACEHOLDER_URL} from 'utils/markdown/apply_markdown'; export function parseHtmlTable(html: string): HTMLTableElement | null { return new DOMParser().parseFromString(html, 'text/html').querySelector('table'); @@ -83,6 +77,13 @@ export function formatMarkdownMessage(clipboardData: DataTransfer, message?: str return {formattedMessage, formattedMarkdown}; } +type FormatGithubCodePasteParams = { + message: string; + clipboardData: DataTransfer; + selectionStart: number | null; + selectionEnd: number | null; +}; + /** * Format the incoming github code paste into a markdown code block. * This function assumes that the clipboardData contains a code block. @@ -90,7 +91,7 @@ export function formatMarkdownMessage(clipboardData: DataTransfer, message?: str * @property {string} formattedMessage - The complete formatted message including the code block. * @property {string} formattedCodeBlock - The resulting code block from the clipboard data. */ -export function formatGithubCodePaste({message, clipboardData, selectionStart, selectionEnd}: FormatMarkdownParams): {formattedMessage: string; formattedCodeBlock: string} { +export function formatGithubCodePaste({message, clipboardData, selectionStart, selectionEnd}: FormatGithubCodePasteParams): {formattedMessage: string; formattedCodeBlock: string} { const isTextSelected = selectionStart !== selectionEnd; const {firstPiece, lastPiece} = isTextSelected ? splitMessageBasedOnTextSelection(selectionStart ?? message.length, selectionEnd ?? message.length, message) : splitMessageBasedOnCaretPosition(selectionStart ?? message.length, message); @@ -104,21 +105,38 @@ export function formatGithubCodePaste({message, clipboardData, selectionStart, s return {formattedMessage, formattedCodeBlock}; } +type FormatMarkdownLinkMessage = { + message: string; + clipboardData: DataTransfer; + selectionStart: number; + selectionEnd: number; +}; + /** * Formats the incoming link paste into a markdown link. * This function assumes that the clipboardData contains a link. * @returns The resulting markdown link from the clipboard data. */ -export function formatMarkdownLinkMessage({message, clipboardData, selectionStart, selectionEnd}: FormatMarkdownParams) { - const isTextSelected = selectionStart !== selectionEnd; +export function formatMarkdownLinkMessage({message, clipboardData, selectionStart, selectionEnd}: FormatMarkdownLinkMessage) { + const selectedText = message.slice(selectionStart, selectionEnd); + const clipboardUrl = clipboardData.getData('text/plain'); - let selectedText = ''; - if (isTextSelected) { - selectedText = message.slice(selectionStart || 0, selectionEnd || 0); + if (selectedText === DEFAULT_PLACEHOLDER_URL) { + if (message.length > DEFAULT_PLACEHOLDER_URL.length) { + const FORMATTED_LINK_URL_PREFIX = ']('; + const FORMATTED_LINK_URL_SUFFIX = ')'; + + const textBefore = message.slice(selectionStart - FORMATTED_LINK_URL_PREFIX.length, selectionStart); + const textAfter = message.slice(selectionEnd, selectionEnd + FORMATTED_LINK_URL_SUFFIX.length); + + // We check "](" "url" ")" to see if user is trying to paste inside of a markdown link + // and selection is on "url" + if (textBefore === FORMATTED_LINK_URL_PREFIX && textAfter === FORMATTED_LINK_URL_SUFFIX) { + return clipboardUrl; + } + } } - const url = clipboardData.getData('text/plain'); - const markdownLink = `[${selectedText}](${url})`; - + const markdownLink = `[${selectedText}](${clipboardUrl})`; return markdownLink; }