diff --git a/app/assets/javascripts/discourse/app/static/prosemirror/extensions/emoji.js b/app/assets/javascripts/discourse/app/static/prosemirror/extensions/emoji.js index 9fc5d5e7d3b..5792de1891f 100644 --- a/app/assets/javascripts/discourse/app/static/prosemirror/extensions/emoji.js +++ b/app/assets/javascripts/discourse/app/static/prosemirror/extensions/emoji.js @@ -131,11 +131,16 @@ const extension = { handler: (state, match, start, end) => { if (emojiExists(match[2])) { const emojiStart = start + match[1].length; - return state.tr.replaceWith( - emojiStart, - end, - state.schema.nodes.emoji.create({ code: match[2] }) - ); + const emojiNode = state.schema.nodes.emoji.create({ code: match[2] }); + const tr = state.tr.replaceWith(emojiStart, end, emojiNode); + + state.doc + .resolve(emojiStart) + .marks() + .forEach((mark) => { + tr.addMark(emojiStart, emojiStart + 1, mark); + }); + return tr; } }, options: { undoable: false }, @@ -148,13 +153,20 @@ const extension = { ), handler: (state, match, start, end) => { const emojiStart = start + match[1].length; - return state.tr - .replaceWith( - emojiStart, - end, - state.schema.nodes.emoji.create({ code: translations[match[2]] }) - ) + const emojiNode = state.schema.nodes.emoji.create({ + code: translations[match[2]], + }); + const tr = state.tr + .replaceWith(emojiStart, end, emojiNode) .insertText(" "); + + state.doc + .resolve(emojiStart) + .marks() + .forEach((mark) => { + tr.addMark(emojiStart, emojiStart + 2, mark); + }); + return tr; }, }, ], diff --git a/spec/system/composer/prosemirror_editor_spec.rb b/spec/system/composer/prosemirror_editor_spec.rb index 54db05a6e20..d64ecd8cf96 100644 --- a/spec/system/composer/prosemirror_editor_spec.rb +++ b/spec/system/composer/prosemirror_editor_spec.rb @@ -806,5 +806,41 @@ describe "Composer - ProseMirror editor", type: :system do expect(rich).to have_no_css(".only-emoji") end + + it "preserves formatting marks when replacing text with emojis using :code: pattern" do + open_composer_and_toggle_rich_editor + + composer.type_content("**bold :smile:**") + + expect(rich).to have_css("strong img.emoji") + expect(rich).to have_css("strong", text: "bold") + + composer.toggle_rich_editor + expect(composer).to have_value("**bold :smile:**") + end + + it "preserves formatting marks when replacing text with emojis using text shortcuts" do + open_composer_and_toggle_rich_editor + + composer.type_content("*italics :) *") + + expect(rich).to have_css("em img.emoji") + expect(rich).to have_css("em", text: "italics") + + composer.toggle_rich_editor + expect(composer).to have_value("*italics :slight_smile:* ") + end + + it "preserves link marks when replacing text with emojis" do + open_composer_and_toggle_rich_editor + + composer.type_content("[link text :heart:](https://example.com)") + + expect(rich).to have_css("a img.emoji") + expect(rich).to have_css("a", text: "link text") + + composer.toggle_rich_editor + expect(composer).to have_value("[link text :heart:](https://example.com)") + end end end