From 4edcb02f94ba832929c054097d2f8badc0a34060 Mon Sep 17 00:00:00 2001 From: Dennis Tang Date: Thu, 4 Oct 2018 08:19:51 +0000 Subject: [PATCH] Resolve "Add status message from within user menu" --- app/assets/javascripts/api.js | 10 + app/assets/javascripts/awards_handler.js | 32 ++- app/assets/javascripts/header.js | 55 ++++ .../javascripts/pages/profiles/show/index.js | 4 +- .../set_status_modal/emoji_menu_in_modal.js | 21 ++ .../javascripts/set_status_modal/event_hub.js | 3 + .../set_status_modal_trigger.vue | 33 +++ .../set_status_modal_wrapper.vue | 241 ++++++++++++++++ app/assets/stylesheets/framework/header.scss | 36 ++- app/assets/stylesheets/framework/mixins.scss | 56 ++++ .../stylesheets/framework/variables.scss | 4 +- app/assets/stylesheets/pages/notes.scss | 54 +--- app/assets/stylesheets/pages/profile.scss | 10 +- .../header/_current_user_dropdown.html.haml | 7 + app/views/layouts/header/_default.html.haml | 3 + ...d-status-message-from-within-user-menu.yml | 5 + doc/user/profile/index.md | 7 + locale/gitlab.pot | 27 ++ .../profiles/user_edit_profile_spec.rb | 266 ++++++++++++++---- 19 files changed, 736 insertions(+), 138 deletions(-) create mode 100644 app/assets/javascripts/set_status_modal/emoji_menu_in_modal.js create mode 100644 app/assets/javascripts/set_status_modal/event_hub.js create mode 100644 app/assets/javascripts/set_status_modal/set_status_modal_trigger.vue create mode 100644 app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue create mode 100644 changelogs/unreleased/49075-add-status-message-from-within-user-menu.yml diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index cd800d75f7a..ecc2440c7e6 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -22,6 +22,7 @@ const Api = { dockerfilePath: '/api/:version/templates/dockerfiles/:key', issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key', usersPath: '/api/:version/users.json', + userStatusPath: '/api/:version/user/status', commitPath: '/api/:version/projects/:id/repository/commits', commitPipelinesPath: '/:project_id/commit/:sha/pipelines', branchSinglePath: '/api/:version/projects/:id/repository/branches/:branch', @@ -266,6 +267,15 @@ const Api = { }); }, + postUserStatus({ emoji, message }) { + const url = Api.buildUrl(this.userStatusPath); + + return axios.put(url, { + emoji, + message, + }); + }, + templates(key, params = {}) { const url = Api.buildUrl(this.templatesPath).replace(':key', key); diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index 5b0c4285339..cace8bb9dba 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -42,10 +42,11 @@ export class AwardsHandler { } bindEvents() { + const $parentEl = this.targetContainerEl ? $(this.targetContainerEl) : $(document); // If the user shows intent let's pre-build the menu this.registerEventListener( 'one', - $(document), + $parentEl, 'mouseenter focus', this.toggleButtonSelector, 'mouseenter focus', @@ -58,7 +59,7 @@ export class AwardsHandler { } }, ); - this.registerEventListener('on', $(document), 'click', this.toggleButtonSelector, e => { + this.registerEventListener('on', $parentEl, 'click', this.toggleButtonSelector, e => { e.stopPropagation(); e.preventDefault(); this.showEmojiMenu($(e.currentTarget)); @@ -76,7 +77,7 @@ export class AwardsHandler { }); const emojiButtonSelector = `.js-awards-block .js-emoji-btn, .${this.menuClass} .js-emoji-btn`; - this.registerEventListener('on', $(document), 'click', emojiButtonSelector, e => { + this.registerEventListener('on', $parentEl, 'click', emojiButtonSelector, e => { e.preventDefault(); const $target = $(e.currentTarget); const $glEmojiElement = $target.find('gl-emoji'); @@ -168,7 +169,8 @@ export class AwardsHandler { `; - document.body.insertAdjacentHTML('beforeend', emojiMenuMarkup); + const targetEl = this.targetContainerEl ? this.targetContainerEl : document.body; + targetEl.insertAdjacentHTML('beforeend', emojiMenuMarkup); this.addRemainingEmojiMenuCategories(); this.setupSearch(); @@ -250,6 +252,12 @@ export class AwardsHandler { } positionMenu($menu, $addBtn) { + if (this.targetContainerEl) { + return $menu.css({ + top: `${$addBtn.outerHeight()}px`, + }); + } + const position = $addBtn.data('position'); // The menu could potentially be off-screen or in a hidden overflow element // So we position the element absolute in the body @@ -424,9 +432,7 @@ export class AwardsHandler { users = origTitle.trim().split(FROM_SENTENCE_REGEX); } users.unshift('You'); - return awardBlock - .attr('title', this.toSentence(users)) - .tooltip('_fixTitle'); + return awardBlock.attr('title', this.toSentence(users)).tooltip('_fixTitle'); } createAwardButtonForVotesBlock(votesBlock, emojiName) { @@ -609,13 +615,11 @@ export class AwardsHandler { let awardsHandlerPromise = null; export default function loadAwardsHandler(reload = false) { if (!awardsHandlerPromise || reload) { - awardsHandlerPromise = import(/* webpackChunkName: 'emoji' */ './emoji').then( - Emoji => { - const awardsHandler = new AwardsHandler(Emoji); - awardsHandler.bindEvents(); - return awardsHandler; - }, - ); + awardsHandlerPromise = import(/* webpackChunkName: 'emoji' */ './emoji').then(Emoji => { + const awardsHandler = new AwardsHandler(Emoji); + awardsHandler.bindEvents(); + return awardsHandler; + }); } return awardsHandlerPromise; } diff --git a/app/assets/javascripts/header.js b/app/assets/javascripts/header.js index 4ae3a714bee..175d0b8498b 100644 --- a/app/assets/javascripts/header.js +++ b/app/assets/javascripts/header.js @@ -1,5 +1,9 @@ import $ from 'jquery'; +import Vue from 'vue'; +import Translate from '~/vue_shared/translate'; import { highCountTrim } from '~/lib/utils/text_utility'; +import SetStatusModalTrigger from './set_status_modal/set_status_modal_trigger.vue'; +import SetStatusModalWrapper from './set_status_modal/set_status_modal_wrapper.vue'; /** * Updates todo counter when todos are toggled. @@ -17,3 +21,54 @@ export default function initTodoToggle() { $todoPendingCount.toggleClass('hidden', parsedCount === 0); }); } + +document.addEventListener('DOMContentLoaded', () => { + const setStatusModalTriggerEl = document.querySelector('.js-set-status-modal-trigger'); + const setStatusModalWrapperEl = document.querySelector('.js-set-status-modal-wrapper'); + + if (setStatusModalTriggerEl || setStatusModalWrapperEl) { + Vue.use(Translate); + + // eslint-disable-next-line no-new + new Vue({ + el: setStatusModalTriggerEl, + data() { + const { hasStatus } = this.$options.el.dataset; + + return { + hasStatus: hasStatus === 'true', + }; + }, + render(createElement) { + return createElement(SetStatusModalTrigger, { + props: { + hasStatus: this.hasStatus, + }, + }); + }, + }); + + // eslint-disable-next-line no-new + new Vue({ + el: setStatusModalWrapperEl, + data() { + const { currentEmoji, currentMessage } = this.$options.el.dataset; + + return { + currentEmoji, + currentMessage, + }; + }, + render(createElement) { + const { currentEmoji, currentMessage } = this; + + return createElement(SetStatusModalWrapper, { + props: { + currentEmoji, + currentMessage, + }, + }); + }, + }); + } +}); diff --git a/app/assets/javascripts/pages/profiles/show/index.js b/app/assets/javascripts/pages/profiles/show/index.js index aea7b649c20..c7ce4675573 100644 --- a/app/assets/javascripts/pages/profiles/show/index.js +++ b/app/assets/javascripts/pages/profiles/show/index.js @@ -11,7 +11,7 @@ document.addEventListener('DOMContentLoaded', () => { const statusEmojiField = document.getElementById('js-status-emoji-field'); const statusMessageField = document.getElementById('js-status-message-field'); - const toggleNoEmojiPlaceholder = (isVisible) => { + const toggleNoEmojiPlaceholder = isVisible => { const placeholderElement = document.getElementById('js-no-emoji-placeholder'); placeholderElement.classList.toggle('hidden', !isVisible); }; @@ -69,5 +69,5 @@ document.addEventListener('DOMContentLoaded', () => { } }); }) - .catch(() => createFlash('Failed to load emoji list!')); + .catch(() => createFlash('Failed to load emoji list.')); }); diff --git a/app/assets/javascripts/set_status_modal/emoji_menu_in_modal.js b/app/assets/javascripts/set_status_modal/emoji_menu_in_modal.js new file mode 100644 index 00000000000..14a89ef9293 --- /dev/null +++ b/app/assets/javascripts/set_status_modal/emoji_menu_in_modal.js @@ -0,0 +1,21 @@ +import { AwardsHandler } from '~/awards_handler'; + +class EmojiMenuInModal extends AwardsHandler { + constructor(emoji, toggleButtonSelector, menuClass, selectEmojiCallback, targetContainerEl) { + super(emoji); + + this.selectEmojiCallback = selectEmojiCallback; + this.toggleButtonSelector = toggleButtonSelector; + this.menuClass = menuClass; + this.targetContainerEl = targetContainerEl; + + this.bindEvents(); + } + + postEmoji($emojiButton, awardUrl, selectedEmoji, callback) { + this.selectEmojiCallback(selectedEmoji, this.emoji.glEmojiTag(selectedEmoji)); + callback(); + } +} + +export default EmojiMenuInModal; diff --git a/app/assets/javascripts/set_status_modal/event_hub.js b/app/assets/javascripts/set_status_modal/event_hub.js new file mode 100644 index 00000000000..0948c2e5352 --- /dev/null +++ b/app/assets/javascripts/set_status_modal/event_hub.js @@ -0,0 +1,3 @@ +import Vue from 'vue'; + +export default new Vue(); diff --git a/app/assets/javascripts/set_status_modal/set_status_modal_trigger.vue b/app/assets/javascripts/set_status_modal/set_status_modal_trigger.vue new file mode 100644 index 00000000000..48e5ede80f2 --- /dev/null +++ b/app/assets/javascripts/set_status_modal/set_status_modal_trigger.vue @@ -0,0 +1,33 @@ + + + diff --git a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue new file mode 100644 index 00000000000..43f0b6651b9 --- /dev/null +++ b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue @@ -0,0 +1,241 @@ + + + diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 11a30d83f03..c430009bfe0 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -529,9 +529,10 @@ } .header-user { - .dropdown-menu { + &.show .dropdown-menu { width: auto; min-width: unset; + max-height: 323px; margin-top: 4px; color: $gl-text-color; left: auto; @@ -542,6 +543,18 @@ .user-name { display: block; } + + .user-status-emoji { + margin-right: 0; + display: block; + vertical-align: text-top; + max-width: 148px; + font-size: 12px; + + gl-emoji { + font-size: $gl-font-size; + } + } } svg { @@ -573,3 +586,24 @@ } } } + +.set-user-status-modal { + .modal-body { + min-height: unset; + } + + .input-lg { + max-width: unset; + } + + .no-emoji-placeholder, + .clear-user-status { + svg { + fill: $gl-text-color-secondary; + } + } + + .emoji-menu-toggle-button { + @include emoji-menu-toggle-button; + } +} diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index 7f37dd3de91..11597c7e85e 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -266,3 +266,59 @@ border-radius: 50%; } } + +@mixin emoji-menu-toggle-button { + line-height: 1; + padding: 0; + min-width: 16px; + color: $gray-darkest; + fill: $gray-darkest; + + .fa { + position: relative; + font-size: 16px; + } + + svg { + @include btn-svg; + margin: 0; + } + + .award-control-icon-positive, + .award-control-icon-super-positive { + position: absolute; + top: 0; + left: 0; + opacity: 0; + } + + &:hover, + &.is-active { + .danger-highlight { + color: $red-500; + } + + .link-highlight { + color: $blue-600; + fill: $blue-600; + } + + .award-control-icon-neutral { + opacity: 0; + } + + .award-control-icon-positive { + opacity: 1; + } + } + + &.is-active { + .award-control-icon-positive { + opacity: 0; + } + + .award-control-icon-super-positive { + opacity: 1; + } + } +} diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 837016db67b..b7a95f604b8 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -314,7 +314,8 @@ $diff-jagged-border-gradient-color: darken($white-normal, 8%); $monospace-font: 'Menlo', 'DejaVu Sans Mono', 'Liberation Mono', 'Consolas', 'Ubuntu Mono', 'Courier New', 'andale mono', 'lucida console', monospace; $regular-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, - 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', + 'Noto Color Emoji'; /* * Dropdowns @@ -634,5 +635,4 @@ Modals */ $modal-body-height: 134px; - $priority-label-empty-state-width: 114px; diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index c9e0899425f..a2070087963 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -519,59 +519,7 @@ ul.notes { } .note-action-button { - line-height: 1; - padding: 0; - min-width: 16px; - color: $gray-darkest; - fill: $gray-darkest; - - .fa { - position: relative; - font-size: 16px; - } - - svg { - @include btn-svg; - margin: 0; - } - - .award-control-icon-positive, - .award-control-icon-super-positive { - position: absolute; - top: 0; - left: 0; - opacity: 0; - } - - &:hover, - &.is-active { - .danger-highlight { - color: $red-500; - } - - .link-highlight { - color: $blue-600; - fill: $blue-600; - } - - .award-control-icon-neutral { - opacity: 0; - } - - .award-control-icon-positive { - opacity: 1; - } - } - - &.is-active { - .award-control-icon-positive { - opacity: 0; - } - - .award-control-icon-super-positive { - opacity: 1; - } - } + @include emoji-menu-toggle-button; } .discussion-toggle-button { diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index caa839c32a5..f084adaf5d3 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -81,14 +81,14 @@ // Middle dot divider between each element in a list of items. .middle-dot-divider { &::after { - content: "\00B7"; // Middle Dot + content: '\00B7'; // Middle Dot padding: 0 6px; font-weight: $gl-font-weight-bold; } &:last-child { &::after { - content: ""; + content: ''; padding: 0; } } @@ -191,7 +191,6 @@ @include media-breakpoint-down(xs) { width: auto; } - } .profile-crop-image-container { @@ -215,7 +214,6 @@ } } - .user-profile { .cover-controls a { margin-left: 5px; @@ -418,7 +416,7 @@ table.u2f-registrations { } &.unverified { - @include status-color($gray-dark, color("gray"), $common-gray-dark); + @include status-color($gray-dark, color('gray'), $common-gray-dark); } } } @@ -431,7 +429,7 @@ table.u2f-registrations { } .emoji-menu-toggle-button { - @extend .note-action-button; + @include emoji-menu-toggle-button; .no-emoji-placeholder { position: relative; diff --git a/app/views/layouts/header/_current_user_dropdown.html.haml b/app/views/layouts/header/_current_user_dropdown.html.haml index 9ed05d6e3d0..261d758622b 100644 --- a/app/views/layouts/header/_current_user_dropdown.html.haml +++ b/app/views/layouts/header/_current_user_dropdown.html.haml @@ -5,7 +5,14 @@ .user-name.bold = current_user.name = current_user.to_reference + - if current_user.status + .user-status-emoji.str-truncated.has-tooltip{ title: current_user.status.message_html, data: { html: 'true', placement: 'bottom' } } + = emoji_icon current_user.status.emoji + = current_user.status.message_html.html_safe %li.divider + - if can?(current_user, :update_user_status, current_user) + %li + .js-set-status-modal-trigger{ data: { has_status: current_user.status.present? ? 'true' : 'false' } } - if current_user_menu?(:profile) %li = link_to s_("CurrentUser|Profile"), current_user, class: 'profile-link', data: { user: current_user.username } diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 044b49c12cc..39604611440 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -74,3 +74,6 @@ %span.sr-only= _('Toggle navigation') = sprite_icon('ellipsis_h', size: 12, css_class: 'more-icon js-navbar-toggle-right') = sprite_icon('close', size: 12, css_class: 'close-icon js-navbar-toggle-left') + +- if can?(current_user, :update_user_status, current_user) + .js-set-status-modal-wrapper{ data: { current_emoji: current_user.status.present? ? current_user.status.emoji : '', current_message: current_user.status.present? ? current_user.status.message : '' } } diff --git a/changelogs/unreleased/49075-add-status-message-from-within-user-menu.yml b/changelogs/unreleased/49075-add-status-message-from-within-user-menu.yml new file mode 100644 index 00000000000..2c65c92dd8b --- /dev/null +++ b/changelogs/unreleased/49075-add-status-message-from-within-user-menu.yml @@ -0,0 +1,5 @@ +--- +title: Set user status from within user menu +merge_request: 21643 +author: +type: added diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md index 8604ea27f99..ab62762f343 100644 --- a/doc/user/profile/index.md +++ b/doc/user/profile/index.md @@ -115,6 +115,13 @@ Please be aware that your status is publicly visible even if your [profile is pr To set your current status: +1. Open the user menu in the top-right corner of the navigation bar. +1. Hit **Set status**, or **Edit status** if you have already set a status. +1. Set the emoji and/or status message to your liking. +1. Hit **Set status**. Alternatively, you can also hit **Remove status** to remove your user status entirely. + +or + 1. Navigate to your personal [profile settings](#profile-settings). 1. In the text field below `Your status`, enter your status message. 1. Select an emoji from the dropdown if you like. diff --git a/locale/gitlab.pot b/locale/gitlab.pot index be002d84f0d..93c7ff32946 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -2722,6 +2722,9 @@ msgstr "" msgid "Failed to check related branches." msgstr "" +msgid "Failed to load emoji list." +msgstr "" + msgid "Failed to remove issue from board, please try again." msgstr "" @@ -5445,6 +5448,30 @@ msgstr "" msgid "SetPasswordToCloneLink|set a password" msgstr "" +msgid "SetStatusModal|Add status emoji" +msgstr "" + +msgid "SetStatusModal|Clear status" +msgstr "" + +msgid "SetStatusModal|Edit status" +msgstr "" + +msgid "SetStatusModal|Remove status" +msgstr "" + +msgid "SetStatusModal|Set a status" +msgstr "" + +msgid "SetStatusModal|Set status" +msgstr "" + +msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later." +msgstr "" + +msgid "SetStatusModal|What's your status?" +msgstr "" + msgid "Settings" msgstr "" diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb index 206a3a4fe9a..e168bb0fc89 100644 --- a/spec/features/profiles/user_edit_profile_spec.rb +++ b/spec/features/profiles/user_edit_profile_spec.rb @@ -61,83 +61,229 @@ describe 'User edit profile' do end context 'user status', :js do - def select_emoji(emoji_name) + def select_emoji(emoji_name, is_modal = false) + emoji_menu_class = is_modal ? '.js-modal-status-emoji-menu' : '.js-status-emoji-menu' 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 = find(%Q{#{emoji_menu_class} .js-emoji-btn gl-emoji[data-name="#{emoji_name}"]}) emoji_button.click end - it 'shows the user status form' do - visit(profile_path) + context 'profile edit form' do + it 'shows the user status form' do + visit(profile_path) - expect(page).to have_content('Current status') - end + expect(page).to have_content('Current status') + end - it 'adds emoji to user status' do - emoji = 'biohazard' - visit(profile_path) - select_emoji(emoji) - submit_settings + 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) + 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 + + 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 '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 + + visit user_path(user) + expect(page).not_to have_selector '.cover-status' + end + + it 'displays a default emoji if only message is entered' do + message = 'a status without emoji' + visit(profile_path) + fill_in 'js-status-message-field', with: message + + within('.js-toggle-emoji-menu') do + expect(page).to have_emoji('speech_balloon') + end 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 + context 'user menu' do + def open_user_status_modal + find('.header-user-dropdown-toggle').click - visit user_path(user) - within('.cover-status') do - expect(page).to have_emoji('speech_balloon') - expect(page).to have_content message - end - end - - 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 '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 + page.within ".header-user" do + click_button 'Set status' + end end - visit(profile_path) - click_button 'js-clear-user-status-button' - submit_settings + def set_user_status_in_modal + page.within "#set-user-status-modal" do + click_button 'Set status' + end + end - visit user_path(user) - expect(page).not_to have_selector '.cover-status' - end + before do + visit root_path(user) + end - it 'displays a default emoji if only message is entered' do - message = 'a status without emoji' - visit(profile_path) - fill_in 'js-status-message-field', with: message + it 'shows the "Set status" menu item in the user menu' do + find('.header-user-dropdown-toggle').click - within('.js-toggle-emoji-menu') do - expect(page).to have_emoji('speech_balloon') + page.within ".header-user" do + expect(page).to have_content('Set status') + end + end + + it 'shows the "Edit status" menu item in the user menu' do + user_status = create(:user_status, user: user, message: 'Eating bread', emoji: 'stuffed_flatbread') + visit root_path(user) + + find('.header-user-dropdown-toggle').click + + page.within ".header-user" do + expect(page).to have_emoji(user_status.emoji) + expect(page).to have_content user_status.message + expect(page).to have_content('Edit status') + end + end + + it 'shows user status modal' do + open_user_status_modal + + expect(page.find('#set-user-status-modal')).to be_visible + expect(page).to have_content('Set a status') + end + + it 'adds emoji to user status' do + emoji = 'biohazard' + open_user_status_modal + select_emoji(emoji, true) + set_user_status_in_modal + + 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' + open_user_status_modal + find('.js-status-message-field').native.send_keys(message) + set_user_status_in_modal + + visit user_path(user) + within('.cover-status') do + expect(page).to have_emoji('speech_balloon') + expect(page).to have_content message + end + end + + it 'adds message and emoji to user status' do + emoji = 'tanabata_tree' + message = 'Playing outside' + open_user_status_modal + select_emoji(emoji, true) + find('.js-status-message-field').native.send_keys(message) + set_user_status_in_modal + + visit user_path(user) + within('.cover-status') do + expect(page).to have_emoji(emoji) + expect(page).to have_content message + end + end + + it 'clears the user status with the "X" button' 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 + + find('.header-user-dropdown-toggle').click + + page.within ".header-user" do + click_button 'Edit status' + end + + find('.js-clear-user-status-button').click + set_user_status_in_modal + + visit user_path(user) + expect(page).not_to have_selector '.cover-status' + end + + it 'clears the user status with the "Remove status" button' 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 + + find('.header-user-dropdown-toggle').click + + page.within ".header-user" do + click_button 'Edit status' + end + + page.within "#set-user-status-modal" do + click_button 'Remove status' + end + + visit user_path(user) + expect(page).not_to have_selector '.cover-status' + end + + it 'displays a default emoji if only message is entered' do + message = 'a status without emoji' + open_user_status_modal + find('.js-status-message-field').native.send_keys(message) + + within('.js-toggle-emoji-menu') do + expect(page).to have_emoji('speech_balloon') + end end end end