FIX: fast edit with a typographic character
When a post containing an apostrophe (') is being cooked, the apostrophe is being converted to the "typographic" version (’) (because we enable markdown-it's **typographer** mode by default in Discourse) When you select text that contains such apostrophe and then try to save your fast edit, it fails miserably without any error. That's because when you select text from the DOM, it uses the cooked version which has the typographic apostrophe. When you save your fast edit, we fetch the raw version of the post, which has the "regular" apostrophe. Thus doing `raw.replace(selectedText, editedText)` doesn't work because `raw` has the regular apostrophe but `selectedText` has the typographic apostrophe. Since it's somewhat complicated to handle all typographic characters, we would basically have to reverse the process done in `custom-typographer-replacements.js`, we instead bail out and show the composer when we detect such character in the selection. Internal ref - t/143836
This commit is contained in:
parent
f35128c6ed
commit
aaec80413d
|
@ -53,6 +53,13 @@ export default class FastEdit extends Component {
|
|||
const result = await ajax(`/posts/${this.args.post.id}`);
|
||||
const newRaw = result.raw.replace(this.args.initialValue, this.value);
|
||||
|
||||
// Warn the user if we failed to update the post
|
||||
if (newRaw === result.raw) {
|
||||
throw new Error(
|
||||
"Failed to update the post. Did your fast edit include a special character?"
|
||||
);
|
||||
}
|
||||
|
||||
await this.args.post.save({ raw: newRaw });
|
||||
} catch (error) {
|
||||
popupAjaxError(error);
|
||||
|
|
|
@ -190,6 +190,7 @@ export default class PostTextSelection extends Component {
|
|||
quoteState.buffer.length === 0 ||
|
||||
quoteState.buffer.includes("|") || // tables are too complex
|
||||
quoteState.buffer.match(/\n/g) || // linebreaks are too complex
|
||||
quoteState.buffer.match(/[‚‘’„“”«»‹›™±…→←↔¶]/g) || // typopgraphic characters are too complex
|
||||
matches?.length !== 1 // duplicates are too complex
|
||||
) {
|
||||
supportsFastEdit = false;
|
||||
|
|
|
@ -11,9 +11,7 @@ import { cloneJSON } from "discourse-common/lib/object";
|
|||
|
||||
acceptance("Fast Edit", function (needs) {
|
||||
needs.user();
|
||||
needs.settings({
|
||||
enable_fast_edit: true,
|
||||
});
|
||||
needs.settings({ enable_fast_edit: true });
|
||||
needs.pretender((server, helper) => {
|
||||
server.get("/posts/419", () => {
|
||||
return helper.response(cloneJSON(postFixtures["/posts/398"]));
|
||||
|
@ -86,6 +84,19 @@ acceptance("Fast Edit", function (needs) {
|
|||
assert.dom(".d-editor-input").exists();
|
||||
});
|
||||
|
||||
test("Opens full composer when selection has typographic characters", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
|
||||
query("#post_2 .cooked").append(`That’s what she said!`);
|
||||
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();
|
||||
});
|
||||
|
||||
test("Works with diacritics", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
|
||||
|
|
|
@ -3,46 +3,54 @@
|
|||
describe "Post selection | Fast edit", type: :system do
|
||||
let(:topic_page) { PageObjects::Pages::Topic.new }
|
||||
let(:fast_editor) { PageObjects::Components::FastEditor.new }
|
||||
|
||||
fab!(:topic)
|
||||
fab!(:post) { Fabricate(:post, topic: topic) }
|
||||
fab!(:post_2) { Fabricate(:post, topic: topic, raw: "It ‘twas a great’ “time”!") }
|
||||
fab!(:spanish_post) { Fabricate(:post, topic: topic, raw: "Hola Juan, ¿cómo estás?") }
|
||||
fab!(:chinese_post) { Fabricate(:post, topic: topic, raw: "这是一个测试") }
|
||||
fab!(:post_with_emoji) { Fabricate(:post, topic: topic, raw: "Good morning :wave:!") }
|
||||
|
||||
fab!(:post) { Fabricate(:post, topic:) }
|
||||
fab!(:post_2) { Fabricate(:post, topic:, raw: "It ‘twas a great’ “time”!") }
|
||||
fab!(:spanish_post) { Fabricate(:post, topic:, raw: "Hola Juan, ¿cómo estás?") }
|
||||
fab!(:chinese_post) { Fabricate(:post, topic:, raw: "这是一个测试") }
|
||||
fab!(:post_with_emoji) { Fabricate(:post, topic:, raw: "Good morning :wave:!") }
|
||||
fab!(:post_with_quote) do
|
||||
Fabricate(
|
||||
:post,
|
||||
topic: topic,
|
||||
topic:,
|
||||
raw: "[quote]\n#{post_2.raw}\n[/quote]\n\nBelle journée, n'est-ce pas ?",
|
||||
)
|
||||
end
|
||||
|
||||
fab!(:current_user) { Fabricate(:admin) }
|
||||
|
||||
before { sign_in(current_user) }
|
||||
|
||||
context "when text selected it opens contact menu and fast editor" do
|
||||
it "opens context menu and fast edit dialog" do
|
||||
def css(post) = "#{topic_page.post_by_number_selector(post.post_number)} .cooked p"
|
||||
|
||||
context "when text is selected" do
|
||||
before do
|
||||
topic_page.visit_topic(topic)
|
||||
|
||||
select_text_range("#{topic_page.post_by_number_selector(1)} .cooked p", 0, 10)
|
||||
expect(topic_page.fast_edit_button).to be_visible
|
||||
|
||||
topic_page.click_fast_edit_button
|
||||
expect(topic_page.fast_edit_input).to be_visible
|
||||
select_text_range(css(post), 0, 5)
|
||||
end
|
||||
|
||||
it "edits first paragraph and saves changes" do
|
||||
topic_page.visit_topic(topic)
|
||||
it "opens context menu" do
|
||||
expect(topic_page.fast_edit_button).to be_visible
|
||||
end
|
||||
|
||||
select_text_range("#{topic_page.post_by_number_selector(1)} .cooked p", 0, 5)
|
||||
topic_page.click_fast_edit_button
|
||||
context "when clicking the fast edit button" do
|
||||
before { topic_page.click_fast_edit_button }
|
||||
|
||||
fast_editor.fill_content("Howdy")
|
||||
fast_editor.save
|
||||
it "opens the fast editor" do
|
||||
expect(topic_page.fast_edit_input).to be_visible
|
||||
end
|
||||
|
||||
within("#post_1 .cooked > p") do |el|
|
||||
expect(el).not_to eq("Hello world")
|
||||
expect(el).to have_content("Howdy")
|
||||
context "when entering some text and clicking the save button" do
|
||||
before do
|
||||
fast_editor.fill_content("Howdy")
|
||||
fast_editor.save
|
||||
end
|
||||
|
||||
it "saves changes" do
|
||||
expect(page).to have_selector(css(post), text: "Howdy world")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -51,7 +59,7 @@ describe "Post selection | Fast edit", type: :system do
|
|||
it "opens the composer directly" do
|
||||
topic_page.visit_topic(topic)
|
||||
|
||||
select_text_range("#{topic_page.post_by_number_selector(6)} .cooked p", 5, 10)
|
||||
select_text_range(css(post_with_quote), 5, 10)
|
||||
topic_page.click_fast_edit_button
|
||||
|
||||
expect(topic_page).to have_expanded_composer
|
||||
|
@ -59,63 +67,56 @@ describe "Post selection | Fast edit", type: :system do
|
|||
end
|
||||
|
||||
context "when editing text that has strange characters" do
|
||||
it "saves when paragraph contains apostrophe" do
|
||||
it "saves when paragraph contains apostrophes" do
|
||||
topic_page.visit_topic(topic)
|
||||
|
||||
select_text_range("#{topic_page.post_by_number_selector(2)} .cooked p", 19, 4)
|
||||
select_text_range(css(post_2), 19, 4)
|
||||
topic_page.click_fast_edit_button
|
||||
|
||||
fast_editor.fill_content("day")
|
||||
fast_editor.save
|
||||
|
||||
expect(page).to have_selector(
|
||||
"#{topic_page.post_by_number_selector(2)} .cooked p",
|
||||
text: "It ‘twas a great’ “day”!",
|
||||
)
|
||||
expect(page).to have_selector(css(post_2), text: "It ‘twas a great’ “day”!")
|
||||
end
|
||||
|
||||
it "saves when text contains diacratics" do
|
||||
it "saves when text contains diacritics" do
|
||||
topic_page.visit_topic(topic)
|
||||
|
||||
select_text_range("#{topic_page.post_by_number_selector(3)} .cooked p", 11, 12)
|
||||
select_text_range(css(spanish_post), 11, 12)
|
||||
|
||||
topic_page.click_fast_edit_button
|
||||
|
||||
fast_editor.fill_content("¿está todo bien?")
|
||||
fast_editor.save
|
||||
|
||||
expect(page).to have_selector(
|
||||
"#{topic_page.post_by_number_selector(3)} .cooked p",
|
||||
text: "Hola Juan, ¿está todo bien?",
|
||||
)
|
||||
expect(page).to have_selector(css(spanish_post), text: "Hola Juan, ¿está todo bien?")
|
||||
end
|
||||
|
||||
it "saves when text contains CJK ranges" do
|
||||
topic_page.visit_topic(topic)
|
||||
|
||||
select_text_range("#{topic_page.post_by_number_selector(4)} .cooked p", 0, 2)
|
||||
select_text_range(css(chinese_post), 0, 2)
|
||||
topic_page.click_fast_edit_button
|
||||
|
||||
fast_editor.fill_content("今天")
|
||||
fast_editor.save
|
||||
|
||||
expect(page).to have_selector(
|
||||
"#{topic_page.post_by_number_selector(4)} .cooked p",
|
||||
text: "今天一个测试",
|
||||
)
|
||||
expect(page).to have_selector(css(chinese_post), text: "今天一个测试")
|
||||
end
|
||||
|
||||
it "saves when text contains emoji" do
|
||||
topic_page.visit_topic(topic)
|
||||
|
||||
select_text_range("#{topic_page.post_by_number_selector(5)} .cooked p", 5, 7)
|
||||
select_text_range(css(post_with_emoji), 5, 7)
|
||||
topic_page.click_fast_edit_button
|
||||
|
||||
fast_editor.fill_content("day")
|
||||
fast_editor.save
|
||||
|
||||
expect(page).to have_no_css("#fast-edit-input")
|
||||
expect(post_with_emoji.raw).to eq("Good day :wave:!")
|
||||
# NOTE: the emoji isn't picked up by the "text:" selector
|
||||
expect(page).to have_selector(css(post_with_emoji), text: "Good day !")
|
||||
# So we also check the raw content to ensure it's been saved correctly
|
||||
expect(post_with_emoji.reload.raw).to eq "Good day :wave:!"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue