From 0009498901ee43bd9b7c7ca6d3c4f1e93628a164 Mon Sep 17 00:00:00 2001 From: Kerry Liu Date: Thu, 18 Nov 2021 08:54:26 -0800 Subject: [PATCH] UX: pasting links on a selection will apply a link format --- .../app/mixins/textarea-text-manipulation.js | 16 ++++++- .../integration/components/d-editor-test.js | 44 ++++++++++++++++++- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/discourse/app/mixins/textarea-text-manipulation.js b/app/assets/javascripts/discourse/app/mixins/textarea-text-manipulation.js index 02563526b13..7e59a34d9d0 100644 --- a/app/assets/javascripts/discourse/app/mixins/textarea-text-manipulation.js +++ b/app/assets/javascripts/discourse/app/mixins/textarea-text-manipulation.js @@ -242,7 +242,8 @@ export default Mixin.create({ let html = clipboard.getData("text/html"); let handled = false; - const { pre, lineVal } = this._getSelected(null, { lineVal: true }); + const selected = this._getSelected(null, { lineVal: true }); + const { pre, value: selectedValue, lineVal } = selected; const isInlinePasting = pre.match(/[^\n]$/); const isCodeBlock = isInside(pre, /(^|\n)```/g); @@ -272,6 +273,19 @@ export default Mixin.create({ } } + if (plainText && !handled && selected.end > selected.start) { + let isURL; + try { + isURL = !!new URL(plainText); + } catch { + isURL = false; + } + if (isURL) { + this._addText(selected, `[${selectedValue}](${plainText})`); + handled = true; + } + } + if (canPasteHtml && !handled) { let markdown = toMarkdown(html); diff --git a/app/assets/javascripts/discourse/tests/integration/components/d-editor-test.js b/app/assets/javascripts/discourse/tests/integration/components/d-editor-test.js index aa37f57817e..3b090b1bb09 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/d-editor-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/d-editor-test.js @@ -729,10 +729,11 @@ third line` }); async function paste(element, text) { - let e = new Event("paste"); + let e = new Event("paste", { cancelable: true }); e.clipboardData = { getData: () => text }; element.dispatchEvent(e); await settled(); + return e; } componentTest("paste table", { @@ -763,6 +764,47 @@ third line` }, }); + testCase( + `pasting a link into a selection applies a link format`, + async function (assert, textarea) { + this.set("value", "See discourse in action"); + setTextareaSelection(textarea, 4, 13); + const element = query(".d-editor"); + const event = await paste(element, "https://www.discourse.org/"); + assert.strictEqual( + this.value, + "See [discourse](https://www.discourse.org/) in action" + ); + assert.strictEqual(event.defaultPrevented, true); + } + ); + + testCase( + `pasting other text into a selection will replace text value`, + async function (assert, textarea) { + this.set("value", "good morning"); + setTextareaSelection(textarea, 5, 12); + const element = query(".d-editor"); + const event = await paste(element, "evening"); + // Synthetic paste events do not manipulate document content. + assert.strictEqual(this.value, "good morning"); + assert.strictEqual(event.defaultPrevented, false); + } + ); + + testCase( + `pasting a url without a selection will insert the url`, + async function (assert, textarea) { + this.set("value", "a link example:"); + jumpEnd(textarea); + const element = query(".d-editor"); + const event = await paste(element, "https://www.discourse.org/"); + // Synthetic paste events do not manipulate document content. + assert.strictEqual(this.value, "a link example:"); + assert.strictEqual(event.defaultPrevented, false); + } + ); + (() => { // Tests to check cursor/selection after replace-text event. const BEFORE = "red green blue";