diff --git a/packages/docusaurus-theme-common/src/utils/__tests__/emojiUtils.test.ts b/packages/docusaurus-theme-common/src/utils/__tests__/emojiUtils.test.ts index 8fdaa6ccc2..9c762c857f 100644 --- a/packages/docusaurus-theme-common/src/utils/__tests__/emojiUtils.test.ts +++ b/packages/docusaurus-theme-common/src/utils/__tests__/emojiUtils.test.ts @@ -64,4 +64,65 @@ describe('extractLeadingEmoji', () => { rest: 'Hello World 😀', }); }); + + it('does not extract single digit', () => { + expect(extractLeadingEmoji('1 Hello World')).toEqual({ + emoji: null, + rest: '1 Hello World', + }); + }); + + it('does not extract multiple digits', () => { + expect(extractLeadingEmoji('11 Hello World')).toEqual({ + emoji: null, + rest: '11 Hello World', + }); + }); + + it('extracts real keycap emoji (digit + VS16 + combining keycap)', () => { + // 1️⃣ is digit "1" followed by U+FE0F (Variation Selector-16) and + // U+20E3 (Combining Enclosing Keycap) — a multi-codepoint grapheme + // that should still be detected as emoji + expect(extractLeadingEmoji('1\u{FE0F}\u{20E3} Something')).toEqual({ + emoji: '1️⃣', + rest: ' Something', + }); + }); + + it('extracts VS16 emoji - ⚠️ (text-default + variation selector)', () => { + // ⚠ (U+26A0) has Emoji=true, Emoji_Presentation=false. With VS16 + // (U+FE0F) it becomes a multi-codepoint grapheme that IS an emoji + expect(extractLeadingEmoji('⚠\u{FE0F} Warning')).toEqual({ + emoji: '⚠\u{FE0F}', + rest: ' Warning', + }); + }); + + it('does not detect text-presentation symbols - ©', () => { + expect(extractLeadingEmoji('© 2026')).toEqual({ + emoji: null, + rest: '© 2026', + }); + }); + + it('does detect ©️', () => { + expect(extractLeadingEmoji('©️ 2026')).toEqual({ + emoji: '©️', + rest: ' 2026', + }); + }); + + it('does not detect text-presentation symbols - ™', () => { + expect(extractLeadingEmoji('™ Brand')).toEqual({ + emoji: null, + rest: '™ Brand', + }); + }); + + it('does not detect text-presentation symbols - ♠', () => { + expect(extractLeadingEmoji('♠ Cards')).toEqual({ + emoji: null, + rest: '♠ Cards', + }); + }); }); diff --git a/packages/docusaurus-theme-common/src/utils/emojiUtils.ts b/packages/docusaurus-theme-common/src/utils/emojiUtils.ts index c66cbfb3fa..cfa1cbde81 100644 --- a/packages/docusaurus-theme-common/src/utils/emojiUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/emojiUtils.ts @@ -29,11 +29,10 @@ export function extractLeadingEmoji(input: string): { return {emoji: null, rest: input}; } - // Leading grapheme contains an emoji (covers flags/ZWJ/skin tones) - if ( - !/\p{Extended_Pictographic}/u.test(grapheme) && - !/\p{Emoji}/u.test(grapheme) - ) { + // Leading grapheme contains an emoji + // Covers flags/ZWJ/skin tones, excludes digits + // See https://github.com/facebook/docusaurus/pull/12072 + if (!/^\p{RGI_Emoji}$/v.test(grapheme)) { return {emoji: null, rest: input}; } diff --git a/project-words.txt b/project-words.txt index 600eddac63..06190d4312 100644 --- a/project-words.txt +++ b/project-words.txt @@ -133,6 +133,8 @@ Kaszubowski Katex katex Kato +Keycap +keycap Keytar keytar Kinsta