From 5d8632d484a405848a3817700f23883060adf366 Mon Sep 17 00:00:00 2001 From: David Battersby Date: Wed, 10 May 2023 12:38:28 +0800 Subject: [PATCH] FIX: fallback to composer for non ascii characters (#21465) The problem When selecting text and clicking the "Edit" button that pops up, this opens up the Fast Edit dialog. The fast edit feature doesn't work well with non standard characters (non-ascii). If the user selects a string of text that contains non-ascii characters, sometimes they won't save. It is non-obvious to the user why this is happening. This issue occurs more frequently when editing content that is written in non-english languages, as fast-edit doesn't work well with non-ascii characters. We currently do a global replace on a couple of the more obvious quotation marks when the fast edit dialog attempts to save, but there are too many edge cases for foreign language content. The solution We can fix this issue by using a catch-all approach for non-ascii characters before the user clicks the edit button to bring up the fast edit dialog. Then we can fallback to the full composer to edit their text, which has much better support for non-ascii characters. What does this regex do? The regex used matches any character that is not within the ASCII range of 0x00 to 0x7F, which includes all control characters and non-ASCII characters. This regex pattern can be used to match any character that is not a standard ASCII character, such as accented characters, non-Latin characters, and special symbols. --- .../discourse/app/components/quote-button.js | 4 ++- .../tests/acceptance/fast-edit-test.js | 28 ++++++++++++++----- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/discourse/app/components/quote-button.js b/app/assets/javascripts/discourse/app/components/quote-button.js index 3a5b9f1b78947..71ce8fb3837b8 100644 --- a/app/assets/javascripts/discourse/app/components/quote-button.js +++ b/app/assets/javascripts/discourse/app/components/quote-button.js @@ -165,12 +165,14 @@ export default Component.extend(KeyEnterEscape, { if (this._canEditPost) { const regexp = new RegExp(escapeRegExp(quoteState.buffer), "gi"); const matches = cooked.innerHTML.match(regexp); + const non_ascii_regex = /[^\x00-\x7F]/; if ( quoteState.buffer.length < 1 || quoteState.buffer.includes("|") || // tables are too complex quoteState.buffer.match(/\n/g) || // linebreaks are too complex - matches?.length > 1 // duplicates are too complex + matches?.length > 1 || // duplicates are too complex + non_ascii_regex.test(quoteState.buffer) // non-ascii chars break fast-edit ) { this.set("_isFastEditable", false); this.set("_fastEditInitialSelection", null); diff --git a/app/assets/javascripts/discourse/tests/acceptance/fast-edit-test.js b/app/assets/javascripts/discourse/tests/acceptance/fast-edit-test.js index 5d02b73a94577..cd1ce99436f8e 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/fast-edit-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/fast-edit-test.js @@ -1,6 +1,5 @@ import { acceptance, - exists, query, selectText, } from "discourse/tests/helpers/qunit-helpers"; @@ -28,7 +27,7 @@ acceptance("Fast Edit", function (needs) { await selectText(textNode, 9); await click(".quote-button .quote-edit-label"); - assert.ok(exists("#fast-edit-input"), "fast editor is open"); + assert.dom("#fast-edit-input").exists(); assert.strictEqual( query("#fast-edit-input").value, "Any plans", @@ -38,7 +37,7 @@ acceptance("Fast Edit", function (needs) { await fillIn("#fast-edit-input", "My edit"); await click(".save-fast-edit"); - assert.notOk(exists("#fast-edit-input"), "fast editor is closed"); + assert.dom("#fast-edit-input").doesNotExist(); }); test("Works with keyboard shortcut", async function (assert) { @@ -49,7 +48,7 @@ acceptance("Fast Edit", function (needs) { await selectText(textNode, 9); await triggerKeyEvent(document, "keypress", "E"); - assert.ok(exists("#fast-edit-input"), "fast editor is open"); + assert.dom("#fast-edit-input").exists(); assert.strictEqual( query("#fast-edit-input").value, "Any plans", @@ -59,7 +58,7 @@ acceptance("Fast Edit", function (needs) { await fillIn("#fast-edit-input", "My edit"); await click(".save-fast-edit"); - assert.notOk(exists("#fast-edit-input"), "fast editor is closed"); + assert.dom("#fast-edit-input").doesNotExist(); }); test("Opens full composer for multi-line selection", async function (assert) { @@ -70,7 +69,22 @@ acceptance("Fast Edit", function (needs) { await selectText(textNode); await click(".quote-button .quote-edit-label"); - assert.notOk(exists("#fast-edit-input"), "fast editor is not open"); - assert.ok(exists(".d-editor-input"), "the composer is open"); + assert.dom("#fast-edit-input").doesNotExist(); + assert.dom(".d-editor-input").exists(); + }); + + test("Opens full composer when editing non-ascii characters", async function (assert) { + await visit("/t/internationalization-localization/280"); + + query("#post_2 .cooked").append( + `Je suis désolé, ”comment ça va”? A bientôt!` + ); + const textNode = query("#post_2 .cooked").childNodes[2]; + + await selectText(textNode); + await click(".quote-button .quote-edit-label"); + + assert.dom("#fast-edit-input").doesNotExist(); + assert.dom(".d-editor-input").exists(); }); });