Commit 4300b228 authored by Toon Claes's avatar Toon Claes

gl-emoji: Move fallback image rendering to frontend

In 2d5b14e3 (Add CustomEmoji Banzai Filter, 2020-11-07) a first
iteration of rendering custom emoji in Banzai was added. That change
inserted <img> tags on the backend and made sure the frontend did not
clean them out.

This change modifies that and passes the fallback image source to the
frontend so it can insert <img> tags in frontend code.
parent 726ef151
import {
initEmojiMap,
getEmojiInfo,
emojiFallbackImageSrc,
emojiImageTag,
FALLBACK_EMOJI_KEY,
} from '../emoji';
import { initEmojiMap, getEmojiInfo, emojiFallbackImageSrc, emojiImageTag } from '../emoji';
import isEmojiUnicodeSupported from '../emoji/support';
class GlEmoji extends HTMLElement {
......@@ -22,10 +16,6 @@ class GlEmoji extends HTMLElement {
if (emojiInfo) {
if (name !== emojiInfo.name) {
if (emojiInfo.name === FALLBACK_EMOJI_KEY && this.innerHTML) {
return; // When fallback emoji is used, but there is a <img> provided, use the <img> instead
}
({ name } = emojiInfo);
this.dataset.name = emojiInfo.name;
}
......@@ -43,34 +33,29 @@ class GlEmoji extends HTMLElement {
this.childNodes &&
Array.prototype.every.call(this.childNodes, (childNode) => childNode.nodeType === 3);
if (
emojiUnicode &&
isEmojiUnicode &&
!isEmojiUnicodeSupported(emojiUnicode, unicodeVersion)
) {
const hasImageFallback = fallbackSrc && fallbackSrc.length > 0;
const hasCssSpriteFallback = fallbackSpriteClass && fallbackSpriteClass.length > 0;
const hasImageFallback = fallbackSrc?.length > 0;
const hasCssSpriteFallback = fallbackSpriteClass?.length > 0;
// CSS sprite fallback takes precedence over image fallback
if (hasCssSpriteFallback) {
if (!gon.emoji_sprites_css_added && gon.emoji_sprites_css_path) {
const emojiSpriteLinkTag = document.createElement('link');
emojiSpriteLinkTag.setAttribute('rel', 'stylesheet');
emojiSpriteLinkTag.setAttribute('href', gon.emoji_sprites_css_path);
document.head.appendChild(emojiSpriteLinkTag);
gon.emoji_sprites_css_added = true;
}
// IE 11 doesn't like adding multiple at once :(
this.classList.add('emoji-icon');
this.classList.add(fallbackSpriteClass);
} else if (hasImageFallback) {
this.innerHTML = '';
this.appendChild(emojiImageTag(name, fallbackSrc));
} else {
const src = emojiFallbackImageSrc(name);
this.innerHTML = '';
this.appendChild(emojiImageTag(name, src));
if (emojiUnicode && isEmojiUnicode && isEmojiUnicodeSupported(emojiUnicode, unicodeVersion)) {
// noop
} else if (hasCssSpriteFallback) {
if (!gon.emoji_sprites_css_added && gon.emoji_sprites_css_path) {
const emojiSpriteLinkTag = document.createElement('link');
emojiSpriteLinkTag.setAttribute('rel', 'stylesheet');
emojiSpriteLinkTag.setAttribute('href', gon.emoji_sprites_css_path);
document.head.appendChild(emojiSpriteLinkTag);
gon.emoji_sprites_css_added = true;
}
// IE 11 doesn't like adding multiple at once :(
this.classList.add('emoji-icon');
this.classList.add(fallbackSpriteClass);
} else if (hasImageFallback) {
this.innerHTML = '';
this.appendChild(emojiImageTag(name, fallbackSrc));
} else {
const src = emojiFallbackImageSrc(name);
this.innerHTML = '';
this.appendChild(emojiImageTag(name, src));
}
});
}
......
......@@ -46,12 +46,13 @@ module Gitlab
def custom_emoji_tag(name, image_source)
data = {
name: name
name: name,
fallback_src: image_source,
unicode_version: 'custom' # Prevents frontend to check for Unicode support
}
options = { title: name, data: data }
ActionController::Base.helpers.content_tag('gl-emoji', title: name, data: data) do
emoji_image_tag(name, image_source).html_safe
end
ActionController::Base.helpers.content_tag('gl-emoji', "", options)
end
end
end
......@@ -77,6 +77,12 @@ describe('gl_emoji', () => {
'<gl-emoji data-name="grey_question" data-unicode-version="6.0" title="white question mark ornament">❔</gl-emoji>',
`<gl-emoji data-name="grey_question" data-unicode-version="6.0" title="white question mark ornament"><img class="emoji" title=":grey_question:" alt=":grey_question:" src="/-/emojis/${EMOJI_VERSION}/grey_question.png" width="20" height="20" align="absmiddle"></gl-emoji>`,
],
[
'custom emoji with image fallback',
'<gl-emoji data-fallback-src="https://cultofthepartyparrot.com/parrots/hd/parrot.gif" data-name="party-parrot" data-unicode-version="custom"></gl-emoji>',
'<gl-emoji data-fallback-src="https://cultofthepartyparrot.com/parrots/hd/parrot.gif" data-name="party-parrot" data-unicode-version="custom"><img class="emoji" title=":party-parrot:" alt=":party-parrot:" src="https://cultofthepartyparrot.com/parrots/hd/parrot.gif" width="20" height="20" align="absmiddle"></gl-emoji>',
'<gl-emoji data-fallback-src="https://cultofthepartyparrot.com/parrots/hd/parrot.gif" data-name="party-parrot" data-unicode-version="custom"><img class="emoji" title=":party-parrot:" alt=":party-parrot:" src="https://cultofthepartyparrot.com/parrots/hd/parrot.gif" width="20" height="20" align="absmiddle"></gl-emoji>',
],
])('%s', (name, markup, withEmojiSupport, withoutEmojiSupport) => {
it(`renders correctly with emoji support`, async () => {
jest.spyOn(EmojiUnicodeSupport, 'default').mockReturnValue(true);
......
......@@ -18,31 +18,30 @@ RSpec.describe Banzai::Filter::CustomEmojiFilter do
doc = filter('<p>:tanuki:</p>', project: project)
expect(doc.css('gl-emoji').first.attributes['title'].value).to eq('tanuki')
expect(doc.css('gl-emoji img').size).to eq 1
end
it 'correctly uses the custom emoji URL' do
doc = filter('<p>:tanuki:</p>')
expect(doc.css('img').first.attributes['src'].value).to eq(custom_emoji.file)
expect(doc.css('gl-emoji').first.attributes['data-fallback-src'].value).to eq(custom_emoji.file)
end
it 'matches multiple same custom emoji' do
doc = filter(':tanuki: :tanuki:')
expect(doc.css('img').size).to eq 2
expect(doc.css('gl-emoji').size).to eq 2
end
it 'matches multiple custom emoji' do
doc = filter(':tanuki: (:happy_tanuki:)')
expect(doc.css('img').size).to eq 2
expect(doc.css('gl-emoji').size).to eq 2
end
it 'does not match enclosed colons' do
doc = filter('tanuki:tanuki:')
expect(doc.css('img').size).to be 0
expect(doc.css('gl-emoji').size).to be 0
end
it 'does not do N+1 query' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment