diff options
author | Winnie Hellmann <winnie@gitlab.com> | 2018-07-31 10:21:53 +0200 |
---|---|---|
committer | Winnie Hellmann <winnie@gitlab.com> | 2018-08-07 21:31:43 +0200 |
commit | 1c10e0147f51198c090af135c2a96f6f6077cebe (patch) | |
tree | 7395df7100a222353525e0ff3a4991881de791ae | |
parent | 313b79d87bd65864307e6864080e12bdbab7c4ab (diff) | |
download | gitlab-ce-1c10e0147f51198c090af135c2a96f6f6077cebe.tar.gz |
Restyle status message input on profile settings
-rw-r--r-- | app/assets/javascripts/awards_handler.js | 55 | ||||
-rw-r--r-- | app/assets/javascripts/pages/profiles/show/emoji_menu.js | 18 | ||||
-rw-r--r-- | app/assets/javascripts/pages/profiles/show/index.js | 49 | ||||
-rw-r--r-- | app/assets/stylesheets/bootstrap_migration.scss | 10 | ||||
-rw-r--r-- | app/assets/stylesheets/pages/notes.scss | 1 | ||||
-rw-r--r-- | app/assets/stylesheets/pages/profile.scss | 20 | ||||
-rw-r--r-- | app/helpers/profiles_helper.rb | 4 | ||||
-rw-r--r-- | app/views/profiles/show.html.haml | 42 | ||||
-rw-r--r-- | changelogs/unreleased/winh-restyle-user-status.yml | 5 | ||||
-rw-r--r-- | locale/gitlab.pot | 16 | ||||
-rw-r--r-- | spec/features/profiles/user_edit_profile_spec.rb | 75 | ||||
-rw-r--r-- | spec/javascripts/pages/profiles/show/emoji_menu_spec.js | 117 |
12 files changed, 360 insertions, 52 deletions
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index 70f20c5c7cf..e34db893989 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -33,19 +33,24 @@ const categoryLabelMap = { const IS_VISIBLE = 'is-visible'; const IS_RENDERED = 'is-rendered'; -class AwardsHandler { +export class AwardsHandler { constructor(emoji) { this.emoji = emoji; this.eventListeners = []; + this.toggleButtonSelector = '.js-add-award'; + this.menuClass = 'js-award-emoji-menu'; + } + + bindEvents() { // If the user shows intent let's pre-build the menu this.registerEventListener( 'one', $(document), 'mouseenter focus', - '.js-add-award', + this.toggleButtonSelector, 'mouseenter focus', () => { - const $menu = $('.emoji-menu'); + const $menu = $(`.${this.menuClass}`); if ($menu.length === 0) { requestAnimationFrame(() => { this.createEmojiMenu(); @@ -53,7 +58,7 @@ class AwardsHandler { } }, ); - this.registerEventListener('on', $(document), 'click', '.js-add-award', e => { + this.registerEventListener('on', $(document), 'click', this.toggleButtonSelector, e => { e.stopPropagation(); e.preventDefault(); this.showEmojiMenu($(e.currentTarget)); @@ -61,15 +66,17 @@ class AwardsHandler { this.registerEventListener('on', $('html'), 'click', e => { const $target = $(e.target); - if (!$target.closest('.emoji-menu').length) { + if (!$target.closest(`.${this.menuClass}`).length) { $('.js-awards-block.current').removeClass('current'); - if ($('.emoji-menu').is(':visible')) { - $('.js-add-award.is-active').removeClass('is-active'); - this.hideMenuElement($('.emoji-menu')); + if ($(`.${this.menuClass}`).is(':visible')) { + $(`${this.toggleButtonSelector}.is-active`).removeClass('is-active'); + this.hideMenuElement($(`.${this.menuClass}`)); } } }); - this.registerEventListener('on', $(document), 'click', '.js-emoji-btn', e => { + + const emojiButtonSelector = `.js-awards-block .js-emoji-btn, .${this.menuClass} .js-emoji-btn`; + this.registerEventListener('on', $(document), 'click', emojiButtonSelector, e => { e.preventDefault(); const $target = $(e.currentTarget); const $glEmojiElement = $target.find('gl-emoji'); @@ -101,7 +108,7 @@ class AwardsHandler { $addBtn.closest('.js-awards-block').addClass('current'); } - const $menu = $('.emoji-menu'); + const $menu = $(`.${this.menuClass}`); const $thumbsBtn = $menu.find('[data-name="thumbsup"], [data-name="thumbsdown"]').parent(); const $userAuthored = this.isUserAuthored($addBtn); if ($menu.length) { @@ -118,7 +125,7 @@ class AwardsHandler { } else { $addBtn.addClass('is-loading is-active'); this.createEmojiMenu(() => { - const $createdMenu = $('.emoji-menu'); + const $createdMenu = $(`.${this.menuClass}`); $addBtn.removeClass('is-loading'); this.positionMenu($createdMenu, $addBtn); return setTimeout(() => { @@ -156,7 +163,7 @@ class AwardsHandler { } const emojiMenuMarkup = ` - <div class="emoji-menu"> + <div class="emoji-menu ${this.menuClass}"> <input type="text" name="emoji-menu-search" value="" class="js-emoji-menu-search emoji-search search-input form-control" placeholder="Search emoji" /> <div class="emoji-menu-content"> @@ -185,7 +192,7 @@ class AwardsHandler { // Avoid the jank and render the remaining categories separately // This will take more time, but makes UI more responsive - const menu = document.querySelector('.emoji-menu'); + const menu = document.querySelector(`.${this.menuClass}`); const emojiContentElement = menu.querySelector('.emoji-menu-content'); const remainingCategories = Object.keys(categoryMap).slice(1); const allCategoriesAddedPromise = remainingCategories.reduce( @@ -270,9 +277,9 @@ class AwardsHandler { if (isInVueNoteablePage() && !isMainAwardsBlock) { const id = votesBlock.attr('id').replace('note_', ''); - this.hideMenuElement($('.emoji-menu')); + this.hideMenuElement($(`.${this.menuClass}`)); - $('.js-add-award.is-active').removeClass('is-active'); + $(`${this.toggleButtonSelector}.is-active`).removeClass('is-active'); const toggleAwardEvent = new CustomEvent('toggleAward', { detail: { awardName: emoji, @@ -291,9 +298,9 @@ class AwardsHandler { return typeof callback === 'function' ? callback() : undefined; }); - this.hideMenuElement($('.emoji-menu')); + this.hideMenuElement($(`.${this.menuClass}`)); - return $('.js-add-award.is-active').removeClass('is-active'); + return $(`${this.toggleButtonSelector}.is-active`).removeClass('is-active'); } addAwardToEmojiBar(votesBlock, emoji, checkForMutuality) { @@ -321,7 +328,7 @@ class AwardsHandler { getVotesBlock() { if (isInVueNoteablePage()) { - const $el = $('.js-add-award.is-active').closest('.note.timeline-entry'); + const $el = $(`${this.toggleButtonSelector}.is-active`).closest('.note.timeline-entry'); if ($el.length) { return $el; @@ -458,7 +465,7 @@ class AwardsHandler { } createEmoji(votesBlock, emoji) { - if ($('.emoji-menu').length) { + if ($(`.${this.menuClass}`).length) { this.createAwardButtonForVotesBlock(votesBlock, emoji); } this.createEmojiMenu(() => { @@ -538,7 +545,7 @@ class AwardsHandler { this.searchEmojis(term); }); - const $menu = $('.emoji-menu'); + const $menu = $(`.${this.menuClass}`); this.registerEventListener('on', $menu, transitionEndEventString, e => { if (e.target === e.currentTarget) { // Clear the search @@ -608,7 +615,7 @@ class AwardsHandler { this.eventListeners.forEach(entry => { entry.element.off.call(entry.element, ...entry.args); }); - $('.emoji-menu').remove(); + $(`.${this.menuClass}`).remove(); } } @@ -616,7 +623,11 @@ let awardsHandlerPromise = null; export default function loadAwardsHandler(reload = false) { if (!awardsHandlerPromise || reload) { awardsHandlerPromise = import(/* webpackChunkName: 'emoji' */ './emoji').then( - Emoji => new AwardsHandler(Emoji), + Emoji => { + const awardsHandler = new AwardsHandler(Emoji); + awardsHandler.bindEvents(); + return awardsHandler; + }, ); } return awardsHandlerPromise; diff --git a/app/assets/javascripts/pages/profiles/show/emoji_menu.js b/app/assets/javascripts/pages/profiles/show/emoji_menu.js new file mode 100644 index 00000000000..094837b40e0 --- /dev/null +++ b/app/assets/javascripts/pages/profiles/show/emoji_menu.js @@ -0,0 +1,18 @@ +import { AwardsHandler } from '~/awards_handler'; + +class EmojiMenu extends AwardsHandler { + constructor(emoji, toggleButtonSelector, menuClass, selectEmojiCallback) { + super(emoji); + + this.selectEmojiCallback = selectEmojiCallback; + this.toggleButtonSelector = toggleButtonSelector; + this.menuClass = menuClass; + } + + postEmoji($emojiButton, awardUrl, selectedEmoji, callback) { + this.selectEmojiCallback(selectedEmoji, this.emoji.glEmojiTag(selectedEmoji)); + callback(); + } +} + +export default EmojiMenu; diff --git a/app/assets/javascripts/pages/profiles/show/index.js b/app/assets/javascripts/pages/profiles/show/index.js new file mode 100644 index 00000000000..949219a0837 --- /dev/null +++ b/app/assets/javascripts/pages/profiles/show/index.js @@ -0,0 +1,49 @@ +import $ from 'jquery'; +import createFlash from '~/flash'; +import GfmAutoComplete from '~/gfm_auto_complete'; +import EmojiMenu from './emoji_menu'; + +document.addEventListener('DOMContentLoaded', () => { + const toggleEmojiMenuButtonSelector = '.js-toggle-emoji-menu'; + const toggleEmojiMenuButton = document.querySelector(toggleEmojiMenuButtonSelector); + const statusEmojiField = document.getElementById('js-status-emoji-field'); + const statusMessageField = document.getElementById('js-status-message-field'); + const findNoEmojiPlaceholder = () => document.getElementById('js-no-emoji-placeholder'); + + const removeStatusEmoji = () => { + const statusEmoji = toggleEmojiMenuButton.querySelector('gl-emoji'); + if (statusEmoji) { + statusEmoji.remove(); + } + }; + + const selectEmojiCallback = (emoji, emojiTag) => { + statusEmojiField.value = emoji; + findNoEmojiPlaceholder().classList.add('hidden'); + removeStatusEmoji(); + toggleEmojiMenuButton.innerHTML += emojiTag; + }; + + const clearEmojiButton = document.getElementById('js-clear-user-status-button'); + clearEmojiButton.addEventListener('click', () => { + statusEmojiField.value = ''; + statusMessageField.value = ''; + removeStatusEmoji(); + findNoEmojiPlaceholder().classList.remove('hidden'); + }); + + const emojiAutocomplete = new GfmAutoComplete(); + emojiAutocomplete.setup($(statusMessageField), { emojis: true }); + + import(/* webpackChunkName: 'emoji' */ '~/emoji') + .then(Emoji => { + const emojiMenu = new EmojiMenu( + Emoji, + toggleEmojiMenuButtonSelector, + 'js-status-emoji-menu', + selectEmojiCallback, + ); + emojiMenu.bindEvents(); + }) + .catch(() => createFlash('Failed to load emoji list!')); +}); diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss index d28ad407734..c20738a20c3 100644 --- a/app/assets/stylesheets/bootstrap_migration.scss +++ b/app/assets/stylesheets/bootstrap_migration.scss @@ -339,3 +339,13 @@ input[type=color].form-control { vertical-align: unset; } } + +// Bootstrap 3 compatibility because bootstrap_form Gem is not updated yet +.input-group-btn:first-child { + @extend .input-group-prepend; +} + +// Bootstrap 3 compatibility because bootstrap_form Gem is not updated yet +.input-group-btn:last-child { + @extend .input-group-append; +} diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 7fc2936c5e6..c369d89d63c 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -546,6 +546,7 @@ ul.notes { svg { @include btn-svg; + margin: 0; } .award-control-icon-positive, diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 5d0d59e12f2..b45e305897c 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -418,3 +418,23 @@ table.u2f-registrations { } } } + +.edit-user { + .clear-user-status { + svg { + fill: $gl-text-color-secondary; + } + } + + .emoji-menu-toggle-button { + @extend .note-action-button; + + .no-emoji-placeholder { + position: relative; + + svg { + fill: $gl-text-color-secondary; + } + } + } +} diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb index a6a57db3002..e7aa92e6e5c 100644 --- a/app/helpers/profiles_helper.rb +++ b/app/helpers/profiles_helper.rb @@ -9,8 +9,4 @@ module ProfilesHelper end end end - - def show_user_status_field? - Feature.enabled?(:user_status_form) || cookies[:feature_user_status_form] == 'true' - end end diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index e7044f722c5..6f08a294c5d 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -31,17 +31,37 @@ %hr = link_to _('Remove avatar'), profile_avatar_path, data: { confirm: _('Avatar will be removed. Are you sure?') }, method: :delete, class: 'btn btn-danger btn-inverted' - - if show_user_status_field? - %hr - .row - .col-lg-4.profile-settings-sidebar - %h4.prepend-top-0= s_("User|Current Status") - %p= s_("Profiles|This emoji and message will appear on your profile and throughout the interface. The message can contain emoji codes, too.") - .col-lg-8 - .row - = f.fields_for :status, @user.status do |status_form| - = status_form.text_field :emoji - = status_form.text_field :message, maxlength: 100 + %hr + .row + .col-lg-4.profile-settings-sidebar + %h4.prepend-top-0= s_("User|Current status") + %p= s_("Profiles|This emoji and message will appear on your profile and throughout the interface.") + .col-lg-8 + = f.fields_for :status, @user.status do |status_form| + - emoji_button = button_tag type: :button, + class: 'js-toggle-emoji-menu emoji-menu-toggle-button btn has-tooltip', + title: s_("Profiles|Add status emoji") do + - if @user.status + = emoji_icon @user.status.emoji + %span#js-no-emoji-placeholder.no-emoji-placeholder{ class: ('hidden' if @user.status) } + = sprite_icon('emoji_slightly_smiling_face', css_class: 'award-control-icon-neutral') + = sprite_icon('emoji_smiley', css_class: 'award-control-icon-positive') + = sprite_icon('emoji_smile', css_class: 'award-control-icon-super-positive') + - reset_message_button = button_tag type: :button, + id: 'js-clear-user-status-button', + class: 'clear-user-status btn has-tooltip', + title: s_("Profiles|Clear status") do + = sprite_icon("close") + + = status_form.hidden_field :emoji, id: 'js-status-emoji-field' + = status_form.text_field :message, + id: 'js-status-message-field', + class: 'form-control input-lg', + label: s_("Profiles|Your status"), + prepend: emoji_button, + append: reset_message_button, + placeholder: s_("Profiles|What's your status?") + %hr .row .col-lg-4.profile-settings-sidebar diff --git a/changelogs/unreleased/winh-restyle-user-status.yml b/changelogs/unreleased/winh-restyle-user-status.yml new file mode 100644 index 00000000000..90370e87825 --- /dev/null +++ b/changelogs/unreleased/winh-restyle-user-status.yml @@ -0,0 +1,5 @@ +--- +title: Restyle status message input on profile settings +merge_request: 20903 +author: +type: changed diff --git a/locale/gitlab.pot b/locale/gitlab.pot index a414f0a90cc..fd06545fcd1 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -4067,9 +4067,15 @@ msgstr "" msgid "Profiles|Add key" msgstr "" +msgid "Profiles|Add status emoji" +msgstr "" + msgid "Profiles|Change username" msgstr "" +msgid "Profiles|Clear status" +msgstr "" + msgid "Profiles|Current path: %{path}" msgstr "" @@ -4097,7 +4103,7 @@ msgstr "" msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?" msgstr "" -msgid "Profiles|This emoji and message will appear on your profile and throughout the interface. The message can contain emoji codes, too." +msgid "Profiles|This emoji and message will appear on your profile and throughout the interface." msgstr "" msgid "Profiles|Type your %{confirmationValue} to confirm:" @@ -4115,6 +4121,9 @@ msgstr "" msgid "Profiles|Username successfully changed" msgstr "" +msgid "Profiles|What's your status?" +msgstr "" + msgid "Profiles|You don't have access to delete this user." msgstr "" @@ -4124,6 +4133,9 @@ msgstr "" msgid "Profiles|Your account is currently an owner in these groups:" msgstr "" +msgid "Profiles|Your status" +msgstr "" + msgid "Profiles|e.g. My MacBook key" msgstr "" @@ -5685,7 +5697,7 @@ msgstr "" msgid "Users" msgstr "" -msgid "User|Current Status" +msgid "User|Current status" msgstr "" msgid "Variables" diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb index 96bbe6f93f1..9e60b4995bd 100644 --- a/spec/features/profiles/user_edit_profile_spec.rb +++ b/spec/features/profiles/user_edit_profile_spec.rb @@ -8,6 +8,10 @@ describe 'User edit profile' do visit(profile_path) end + def submit_settings + click_button 'Update profile settings' + end + it 'changes user profile' do fill_in 'user_skype', with: 'testskype' fill_in 'user_linkedin', with: 'testlinkedin' @@ -16,7 +20,7 @@ describe 'User edit profile' do fill_in 'user_location', with: 'Ukraine' fill_in 'user_bio', with: 'I <3 GitLab' fill_in 'user_organization', with: 'GitLab' - click_button 'Update profile settings' + submit_settings expect(user.reload).to have_attributes( skype: 'testskype', @@ -34,7 +38,7 @@ describe 'User edit profile' do context 'user avatar' do before do attach_file(:user_avatar, Rails.root.join('spec', 'fixtures', 'banana_sample.gif')) - click_button 'Update profile settings' + submit_settings end it 'changes user avatar' do @@ -56,30 +60,75 @@ describe 'User edit profile' do end end - context 'user status' do - it 'hides user status when the feature is disabled' do - stub_feature_flags(user_status_form: false) + context 'user status', :js do + def select_emoji(emoji_name) + toggle_button = find('.js-toggle-emoji-menu') + toggle_button.click + emoji_button = find(%Q{.js-status-emoji-menu .js-emoji-btn gl-emoji[data-name="#{emoji_name}"]}) + emoji_button.click + end + it 'shows the user status form' do visit(profile_path) - expect(page).not_to have_content('Current Status') + expect(page).to have_content('Current status') end - it 'shows the status form when the feature is enabled' do - stub_feature_flags(user_status_form: true) + it 'adds emoji to user status' do + emoji = 'biohazard' + visit(profile_path) + select_emoji(emoji) + submit_settings + visit user_path(user) + within('.cover-status') do + expect(page).to have_emoji(emoji) + end + end + + it 'adds message to user status' do + message = 'I have something to say' visit(profile_path) + fill_in 'js-status-message-field', with: message + submit_settings + + visit user_path(user) + within('.cover-status') do + expect(page).to have_emoji('speech_balloon') + expect(page).to have_content message + end + end - expect(page).to have_content('Current Status') + it 'adds message and emoji to user status' do + emoji = 'tanabata_tree' + message = 'Playing outside' + visit(profile_path) + select_emoji(emoji) + fill_in 'js-status-message-field', with: message + submit_settings + + visit user_path(user) + within('.cover-status') do + expect(page).to have_emoji(emoji) + expect(page).to have_content message + end end - it 'shows the status form when the feature is enabled by setting a cookie', :js do - stub_feature_flags(user_status_form: false) - set_cookie('feature_user_status_form', 'true') + it 'clears the user status' do + user_status = create(:user_status, user: user, message: 'Eating bread', emoji: 'stuffed_flatbread') + + visit user_path(user) + within('.cover-status') do + expect(page).to have_emoji(user_status.emoji) + expect(page).to have_content user_status.message + end visit(profile_path) + click_button 'js-clear-user-status-button' + submit_settings - expect(page).to have_content('Current Status') + visit user_path(user) + expect(page).not_to have_selector '.cover-status' end end end diff --git a/spec/javascripts/pages/profiles/show/emoji_menu_spec.js b/spec/javascripts/pages/profiles/show/emoji_menu_spec.js new file mode 100644 index 00000000000..b70368fc92f --- /dev/null +++ b/spec/javascripts/pages/profiles/show/emoji_menu_spec.js @@ -0,0 +1,117 @@ +import $ from 'jquery'; +import axios from '~/lib/utils/axios_utils'; +import EmojiMenu from '~/pages/profiles/show/emoji_menu'; +import { TEST_HOST } from 'spec/test_constants'; + +describe('EmojiMenu', () => { + const dummyEmojiTag = '<dummy></tag>'; + const dummyToggleButtonSelector = '.toggle-button-selector'; + const dummyMenuClass = 'dummy-menu-class'; + + let emojiMenu; + let dummySelectEmojiCallback; + let dummyEmojiList; + + beforeEach(() => { + dummySelectEmojiCallback = jasmine.createSpy('dummySelectEmojiCallback'); + dummyEmojiList = { + glEmojiTag() { + return dummyEmojiTag; + }, + normalizeEmojiName(emoji) { + return emoji; + }, + isEmojiNameValid() { + return true; + }, + getEmojiCategoryMap() { + return { dummyCategory: [] }; + }, + }; + + emojiMenu = new EmojiMenu( + dummyEmojiList, + dummyToggleButtonSelector, + dummyMenuClass, + dummySelectEmojiCallback, + ); + }); + + afterEach(() => { + emojiMenu.destroy(); + }); + + describe('addAward', () => { + const dummyAwardUrl = `${TEST_HOST}/award/url`; + const dummyEmoji = 'tropical_fish'; + const dummyVotesBlock = () => $('<div />'); + + it('calls selectEmojiCallback', done => { + expect(dummySelectEmojiCallback).not.toHaveBeenCalled(); + + emojiMenu.addAward(dummyVotesBlock(), dummyAwardUrl, dummyEmoji, false, () => { + expect(dummySelectEmojiCallback).toHaveBeenCalledWith(dummyEmoji, dummyEmojiTag); + done(); + }); + }); + + it('does not make an axios requst', done => { + spyOn(axios, 'request').and.stub(); + + emojiMenu.addAward(dummyVotesBlock(), dummyAwardUrl, dummyEmoji, false, () => { + expect(axios.request).not.toHaveBeenCalled(); + done(); + }); + }); + }); + + describe('bindEvents', () => { + beforeEach(() => { + spyOn(emojiMenu, 'registerEventListener').and.stub(); + }); + + it('binds event listeners to custom toggle button', () => { + emojiMenu.bindEvents(); + + expect(emojiMenu.registerEventListener).toHaveBeenCalledWith( + 'one', + jasmine.anything(), + 'mouseenter focus', + dummyToggleButtonSelector, + 'mouseenter focus', + jasmine.anything(), + ); + expect(emojiMenu.registerEventListener).toHaveBeenCalledWith( + 'on', + jasmine.anything(), + 'click', + dummyToggleButtonSelector, + jasmine.anything(), + ); + }); + + it('binds event listeners to custom menu class', () => { + emojiMenu.bindEvents(); + + expect(emojiMenu.registerEventListener).toHaveBeenCalledWith( + 'on', + jasmine.anything(), + 'click', + `.js-awards-block .js-emoji-btn, .${dummyMenuClass} .js-emoji-btn`, + jasmine.anything(), + ); + }); + }); + + describe('createEmojiMenu', () => { + it('renders the menu with custom menu class', () => { + const menuElement = () => + document.body.querySelector(`.emoji-menu.${dummyMenuClass} .emoji-menu-content`); + expect(menuElement()).toBe(null); + + emojiMenu.createEmojiMenu(); + + expect(menuElement()).not.toBe(null); + }); + }); +}); |