From 2f9c2149a38e8a4067cb50c1cd1bbb1e72c263b3 Mon Sep 17 00:00:00 2001 From: ZJ van de Weg Date: Wed, 25 May 2016 21:07:36 +0200 Subject: Backend awardables on comments --- app/assets/javascripts/notes.js.coffee | 2 +- app/controllers/concerns/toggle_award_emoji.rb | 11 ++++++- app/controllers/projects/notes_controller.rb | 3 ++ app/models/note.rb | 1 + spec/controllers/projects/notes_controller_spec.rb | 36 ++++++++++++++++++++++ spec/models/note_spec.rb | 10 ++++++ 6 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 spec/controllers/projects/notes_controller_spec.rb diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 7c3d57fc194..101154114ba 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -162,7 +162,7 @@ class @Notes renderNote: (note) -> unless note.valid if note.award - flash = new Flash('You have already used this award emoji!', 'alert') + flash = new Flash('You have already awarded this emoji!', 'alert') flash.pinTo('.header-content') return diff --git a/app/controllers/concerns/toggle_award_emoji.rb b/app/controllers/concerns/toggle_award_emoji.rb index 09ff44f291b..036777c80c1 100644 --- a/app/controllers/concerns/toggle_award_emoji.rb +++ b/app/controllers/concerns/toggle_award_emoji.rb @@ -9,13 +9,22 @@ module ToggleAwardEmoji name = params.require(:name) awardable.toggle_award_emoji(name, current_user) - TodoService.new.new_award_emoji(awardable, current_user) + TodoService.new.new_award_emoji(to_todoable(awardable), current_user) render json: { ok: true } end private + def to_todoable(awardable) + case awardable + when Note + awardable.noteable + else + awardable + end + end + def awardable raise NotImplementedError end diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index c205474e999..836f79ff080 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -1,4 +1,6 @@ class Projects::NotesController < Projects::ApplicationController + include ToggleAwardEmoji + # Authorize before_action :authorize_read_note! before_action :authorize_create_note!, only: [:create] @@ -61,6 +63,7 @@ class Projects::NotesController < Projects::ApplicationController def note @note ||= @project.notes.find(params[:id]) end + alias_method :awardable, :note def note_to_html(note) render_to_string( diff --git a/app/models/note.rb b/app/models/note.rb index 46c3f6e24af..585d8c4ad84 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -3,6 +3,7 @@ class Note < ActiveRecord::Base include Gitlab::CurrentSettings include Participable include Mentionable + include Awardable default_value_for :system, false diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb new file mode 100644 index 00000000000..cb7d04a059c --- /dev/null +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -0,0 +1,36 @@ +require('spec_helper') + +describe Projects::NotesController do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:issue) { create(:issue, project: project) } + let(:note) { create(:note, noteable: issue, project: project) } + + describe 'POST #toggle_award_emoji' do + before do + sign_in(user) + project.team << [user, :developer] + end + + it "toggles the award emoji" do + expect do + post(:toggle_award_emoji, namespace_id: project.namespace.path, + project_id: project.path, id: note.id, name: "thumbsup") + end.to change { AwardEmoji.count }.by(1) + + expect(response.status).to eq(200) + end + + it "removes the already let award emoji" do + post(:toggle_award_emoji, namespace_id: project.namespace.path, + project_id: project.path, id: note.id, name: "thumbsup") + + expect do + post(:toggle_award_emoji, namespace_id: project.namespace.path, + project_id: project.path, id: note.id, name: "thumbsup") + end.to change { AwardEmoji.count }.by(-1) + + expect(response.status).to eq(200) + end + end +end diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 139f7cb9783..f15e96714b2 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -9,6 +9,16 @@ describe Note, models: true do it { is_expected.to have_many(:todos).dependent(:destroy) } end + describe 'modules' do + subject { described_class } + + it { is_expected.to include_module(Participable) } + it { is_expected.to include_module(Mentionable) } + it { is_expected.to include_module(Awardable) } + + it { is_expected.to include_module(Gitlab::CurrentSettings) } + end + describe 'validation' do it { is_expected.to validate_presence_of(:note) } it { is_expected.to validate_presence_of(:project) } -- cgit v1.2.1 From eaff5afc9b50f41aceae32282e92f6450062a074 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Thu, 26 May 2016 12:15:20 +0300 Subject: Show emoji menu in notes. --- app/assets/javascripts/awards_handler.coffee | 9 ++++++--- app/assets/stylesheets/pages/notes.scss | 16 ++++++++++++++++ app/views/projects/notes/_note.html.haml | 3 +++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 766c653111a..e211c86f425 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -51,7 +51,7 @@ class @AwardsHandler $('#emoji_search').focus() else $addBtn.addClass 'is-loading is-active' - url = $addBtn.data 'award-menu-url' + url = @getAwardMenuUrl() @createEmojiMenu url, => $addBtn.removeClass 'is-loading' @@ -74,6 +74,7 @@ class @AwardsHandler positionMenu: ($menu, $addBtn) -> + position = $addBtn.data('position') # The menu could potentially be off-screen or in a hidden overflow element @@ -240,8 +241,10 @@ class @AwardsHandler return @createEmoji_ emoji if $('.emoji-menu').length - awardMenuUrl = gl.awardMenuUrl or '/emojis' - @createEmojiMenu awardMenuUrl, => @createEmoji emoji + @createEmojiMenu @getAwardMenuUrl(), => @createEmoji emoji + + + getAwardMenuUrl: -> return gl.awardMenuUrl or '/emojis' resolveNameToCssClass: (emoji) -> diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index a3e1ac13a43..3acc9152c01 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -120,6 +120,22 @@ ul.notes { padding-bottom: 3px; } + .note-emoji-button { + .fa-spinner { + display: none; + } + + &.is-loading { + .fa-smile-o { + display: none; + } + + .fa-spinner { + display: inline-block; + } + } + } + } } diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index f1045bbd8c3..c176778c515 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -22,6 +22,9 @@ %span.note-role = access - if note_editable + = link_to '#', title: 'Add Reaction', class: 'note-emoji-button js-add-award js-note-emoji', data: { position: 'right' } do + = icon('spinner spin') + = icon('smile-o') = link_to '#', title: 'Edit comment', class: 'note-action-button js-note-edit' do = icon('pencil') = link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button js-note-delete danger' do -- cgit v1.2.1 From f261be0949bb589b54359b8fba2bcee9872e69cf Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Fri, 27 May 2016 18:29:27 +0300 Subject: Award emoji implementation for notes. --- app/assets/javascripts/awards_handler.coffee | 151 +++++++++++++++----------- app/views/award_emoji/_awards_block.html.haml | 2 +- app/views/projects/notes/_note.html.haml | 4 +- 3 files changed, 92 insertions(+), 65 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index e211c86f425..071dbd36641 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -6,35 +6,43 @@ class @AwardsHandler $(document) .off 'click', '.js-add-award' - .on 'click', '.js-add-award', (event) => - event.stopPropagation() - event.preventDefault() + .on 'click', '.js-add-award', (e) => + e.stopPropagation() + e.preventDefault() - @showEmojiMenu $(event.currentTarget) + @showEmojiMenu $(e.currentTarget) - $('html').on 'click', (event) -> - unless $(event.target).closest('.emoji-menu').length + $('html').on 'click', (e) -> + $target = $ e.target + + unless $target.closest('.emoji-menu-content').length + $('.js-awards-block.current').removeClass 'current' + + unless $target.closest('.emoji-menu').length if $('.emoji-menu').is(':visible') $('.js-add-award.is-active').removeClass 'is-active' $('.emoji-menu').removeClass 'is-visible' $(document) .off 'click', '.js-emoji-btn' - .on 'click', '.js-emoji-btn', @handleClick - + .on 'click', '.js-emoji-btn', (e) => + e.preventDefault() - handleClick: (e) => + $target = $ e.currentTarget + emoji = $target.find('.icon').data 'emoji' - e.preventDefault() - - emoji = $(e.currentTarget).find('.icon').data 'emoji' - @getVotesBlock().addClass 'js-awards-block' - @addAward @getAwardUrl(), emoji + $target.closest('.js-awards-block').addClass 'current' + @addAward @getAwardUrl(), emoji showEmojiMenu: ($addBtn) -> - $menu = $('.emoji-menu') + $menu = $ '.emoji-menu' + + if $addBtn.hasClass 'note-emoji-button' + $addBtn.parents('.note').find('.js-awards-block').addClass 'current' + else + $addBtn.closest('.js-awards-block').addClass 'current' if $menu.length $holder = $addBtn.closest('.js-award-holder') @@ -68,7 +76,7 @@ class @AwardsHandler createEmojiMenu: (awardMenuUrl, callback) -> - $.get awardMenuUrl, (response) => + $.get awardMenuUrl, (response) -> $('body').append response callback() @@ -94,11 +102,10 @@ class @AwardsHandler addAward: (awardUrl, emoji, checkMutuality = yes) -> - emoji = @normilizeEmojiName(emoji) - @postEmoji awardUrl, emoji, => - @addAwardToEmojiBar(emoji, checkMutuality) + emoji = @normilizeEmojiName emoji - $('.js-awards-block-current').removeClass 'js-awards-block-current' + @postEmoji awardUrl, emoji, => + @addAwardToEmojiBar emoji, checkMutuality $('.emoji-menu').removeClass 'is-visible' @@ -108,25 +115,26 @@ class @AwardsHandler @checkMutuality emoji if checkForMutuality @addEmojiToFrequentlyUsedList(emoji) - emoji = @normilizeEmojiName(emoji) + emoji = @normilizeEmojiName(emoji) $emojiBtn = @findEmojiIcon(emoji).parent() if $emojiBtn.length > 0 - if @isActive($emojiBtn) - @decrementCounter($emojiBtn, emoji) + if @isActive $emojiBtn + @decrementCounter $emojiBtn, emoji else - counter = $emojiBtn.find('.js-counter') - counter.text(parseInt(counter.text()) + 1) - $emojiBtn.addClass('active') - @addMeToUserList(emoji) + counter = $emojiBtn.find '.js-counter' + counter.text parseInt(counter.text()) + 1 + $emojiBtn.addClass 'active' + @addMeToUserList emoji else - @createEmoji(emoji) + @getVotesBlock().removeClass 'hidden' + @createEmoji emoji - getVotesBlock: -> return $ '.awards.js-awards-block' + getVotesBlock: -> return $ '.js-awards-block.current' - getAwardUrl: -> @getVotesBlock().data 'award-url' + getAwardUrl: -> return @getVotesBlock().data 'award-url' checkMutuality: (emoji) -> @@ -135,8 +143,9 @@ class @AwardsHandler if emoji in [ 'thumbsup', 'thumbsdown' ] mutualVote = if emoji is 'thumbsup' then 'thumbsdown' else 'thumbsup' + selector = "[data-emoji=#{mutualVote}]" - isAlreadyVoted = $("[data-emoji=#{mutualVote}]").parent().hasClass 'active' + isAlreadyVoted = @getVotesBlock().find(selector).parent().hasClass 'active' @addAward awardUrl, mutualVote, no if isAlreadyVoted @@ -144,28 +153,35 @@ class @AwardsHandler decrementCounter: ($emojiBtn, emoji) -> - isntNoteBody = $emojiBtn.closest('.note-body').length is 0 - counter = $('.js-counter', $emojiBtn) - counterNumber = parseInt(counter.text()) - if !isntNoteBody - # If this is a note body, we just hide the award emoji row like the initial state - $emojiBtn.closest('.js-awards-block').addClass 'hidden' + counter = $('.js-counter', $emojiBtn) + counterNumber = parseInt(counter.text()) if counterNumber > 1 counter.text(counterNumber - 1) @removeMeFromUserList($emojiBtn, emoji) - else if (emoji == 'thumbsup' || emoji == 'thumbsdown') && isntNoteBody + else if emoji is 'thumbsup' or emoji is 'thumbsdown' $emojiBtn.tooltip('destroy') counter.text('0') @removeMeFromUserList($emojiBtn, emoji) + @removeEmoji $emojiBtn if $emojiBtn.parents('.note').length else - $emojiBtn.tooltip('destroy') - $emojiBtn.remove() + @removeEmoji $emojiBtn $emojiBtn.removeClass('active') + removeEmoji: ($emojiBtn) -> + + $emojiBtn.tooltip('destroy') + $emojiBtn.remove() + + $votesBlock = @getVotesBlock() + + if $votesBlock.find('.js-emoji-btn').length is 0 + $votesBlock.addClass 'hidden' + + getAwardTooltip: ($awardBlock) -> return $awardBlock.attr('data-original-title') or $awardBlock.attr('data-title') @@ -225,16 +241,12 @@ class @AwardsHandler " emoji_node = $(buttonHtml) - .insertBefore '.js-awards-block .js-award-holder:not(.js-award-action-btn)' + .insertBefore '.js-awards-block.current .js-award-holder:not(.js-award-action-btn)' .find '.emoji-icon' .data 'emoji', emoji $('.award-control').tooltip() - - $currentBlock = $ '.js-awards-block' - - if $currentBlock.is '.hidden' - $currentBlock.removeClass 'hidden' + $('.js-awards-block.current').removeClass 'current' createEmoji: (emoji) -> @@ -261,42 +273,53 @@ class @AwardsHandler postEmoji: (awardUrl, emoji, callback) -> + $.post awardUrl, { name: emoji }, (data) -> - if data.ok - callback.call() + callback.call() if data.ok + findEmojiIcon: (emoji) -> - $(".js-awards-block.awards > .js-emoji-btn [data-emoji='#{emoji}']") + + return $(".js-awards-block.current > .js-emoji-btn [data-emoji='#{emoji}']") + scrollToAwards: -> - $('body, html').animate({ - scrollTop: $('.awards').offset().top - 80 - }, 200) - normilizeEmojiName: (emoji) -> - @aliases[emoji] || emoji + options = scrollTop: $('.awards').offset().top - 80 + $('body, html').animate options, 200 + + + normilizeEmojiName: (emoji) -> return @aliases[emoji] or emoji + addEmojiToFrequentlyUsedList: (emoji) -> - frequently_used_emojis = @getFrequentlyUsedEmojis() - frequently_used_emojis.push(emoji) - $.cookie('frequently_used_emojis', frequently_used_emojis.join(','), { expires: 365 }) + + frequentlyUsedEmojis = @getFrequentlyUsedEmojis() + frequentlyUsedEmojis.push emoji + $.cookie 'frequently_used_emojis', frequentlyUsedEmojis.join(','), { expires: 365 } + getFrequentlyUsedEmojis: -> - frequently_used_emojis = ($.cookie('frequently_used_emojis') || '').split(',') - _.compact(_.uniq(frequently_used_emojis)) + + frequentlyUsedEmojis = ($.cookie('frequently_used_emojis') or '').split(',') + return _.compact _.uniq frequentlyUsedEmojis + renderFrequentlyUsedBlock: -> - if $.cookie('frequently_used_emojis') - frequently_used_emojis = @getFrequentlyUsedEmojis() + + if $.cookie 'frequently_used_emojis' + frequentlyUsedEmojis = @getFrequentlyUsedEmojis() ul = $("
    ") - for emoji in frequently_used_emojis + for emoji in frequentlyUsedEmojis $(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul) $('input.emoji-search').after(ul).after($('
    ').text('Frequently used')) + setupSearch: -> + $('input.emoji-search').on 'keyup', (ev) => term = $(ev.target).val() @@ -313,5 +336,7 @@ class @AwardsHandler else $('.emoji-menu-content').children().show() - searchEmojis: (term)-> + + searchEmojis: (term) -> + $(".emoji-menu-content [data-emoji*='#{term}']").closest('li').clone() diff --git a/app/views/award_emoji/_awards_block.html.haml b/app/views/award_emoji/_awards_block.html.haml index e9302c39753..84fd146a26b 100644 --- a/app/views/award_emoji/_awards_block.html.haml +++ b/app/views/award_emoji/_awards_block.html.haml @@ -11,7 +11,7 @@ gl.awardMenuUrl = "#{emojis_path}" .award-menu-holder.js-award-holder - %button.btn.award-control.js-add-award{ type: "button", data: { award_menu_url: emojis_path } } + %button.btn.award-control.js-add-award{ type: "button" } = icon('smile-o', class: "award-control-icon award-control-icon-normal") = icon('spinner spin', class: "award-control-icon award-control-icon-loading") %span.award-control-text diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index c176778c515..47ab8906316 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -22,7 +22,7 @@ %span.note-role = access - if note_editable - = link_to '#', title: 'Add Reaction', class: 'note-emoji-button js-add-award js-note-emoji', data: { position: 'right' } do + = link_to '#', title: 'Award Emoji', class: 'note-emoji-button js-add-award js-note-emoji', data: { position: 'right' } do = icon('spinner spin') = icon('smile-o') = link_to '#', title: 'Edit comment', class: 'note-action-button js-note-edit' do @@ -35,6 +35,8 @@ = markdown(note.note, pipeline: :note, cache_key: [note, "note"], author: note.author) - if note_editable = render 'projects/notes/edit_form', note: note + .note-awards + = render 'award_emoji/awards_block', awardable: note, inline: false = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true) - if note.attachment.url -- cgit v1.2.1 From 40c86c916e252dd3556c0dc526e6a9de505e5de3 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Sat, 28 May 2016 01:49:18 +0300 Subject: Refactor awardsHandler methods to work with votesBlock. --- app/assets/javascripts/awards_handler.coffee | 68 ++++++++++++++-------------- app/assets/javascripts/notes.js.coffee | 3 +- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 071dbd36641..27b2853e7d1 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -32,7 +32,7 @@ class @AwardsHandler emoji = $target.find('.icon').data 'emoji' $target.closest('.js-awards-block').addClass 'current' - @addAward @getAwardUrl(), emoji + @addAward @getVotesBlock(), @getAwardUrl(), emoji showEmojiMenu: ($addBtn) -> @@ -100,23 +100,23 @@ class @AwardsHandler $menu.css(css) - addAward: (awardUrl, emoji, checkMutuality = yes) -> + addAward: (votesBlock, awardUrl, emoji, checkMutuality = yes) -> emoji = @normilizeEmojiName emoji @postEmoji awardUrl, emoji, => - @addAwardToEmojiBar emoji, checkMutuality + @addAwardToEmojiBar votesBlock, emoji, checkMutuality $('.emoji-menu').removeClass 'is-visible' - addAwardToEmojiBar: (emoji, checkForMutuality = yes) -> + addAwardToEmojiBar: (votesBlock, emoji, checkForMutuality = yes) -> - @checkMutuality emoji if checkForMutuality - @addEmojiToFrequentlyUsedList(emoji) + @checkMutuality votesBlock, emoji if checkForMutuality + @addEmojiToFrequentlyUsedList emoji - emoji = @normilizeEmojiName(emoji) - $emojiBtn = @findEmojiIcon(emoji).parent() + emoji = @normilizeEmojiName emoji + $emojiBtn = @findEmojiIcon(votesBlock, emoji).parent() if $emojiBtn.length > 0 if @isActive $emojiBtn @@ -125,10 +125,10 @@ class @AwardsHandler counter = $emojiBtn.find '.js-counter' counter.text parseInt(counter.text()) + 1 $emojiBtn.addClass 'active' - @addMeToUserList emoji + @addMeToUserList votesBlock, emoji else - @getVotesBlock().removeClass 'hidden' - @createEmoji emoji + votesBlock.removeClass 'hidden' + @createEmoji votesBlock, emoji getVotesBlock: -> return $ '.js-awards-block.current' @@ -137,7 +137,7 @@ class @AwardsHandler getAwardUrl: -> return @getVotesBlock().data 'award-url' - checkMutuality: (emoji) -> + checkMutuality: (votesBlock, emoji) -> awardUrl = @getAwardUrl() @@ -145,8 +145,8 @@ class @AwardsHandler mutualVote = if emoji is 'thumbsup' then 'thumbsdown' else 'thumbsup' selector = "[data-emoji=#{mutualVote}]" - isAlreadyVoted = @getVotesBlock().find(selector).parent().hasClass 'active' - @addAward awardUrl, mutualVote, no if isAlreadyVoted + isAlreadyVoted = votesBlock.find(selector).parent().hasClass 'active' + @addAward votesBlock, awardUrl, mutualVote, no if isAlreadyVoted isActive: ($emojiBtn) -> $emojiBtn.hasClass 'active' @@ -155,20 +155,20 @@ class @AwardsHandler decrementCounter: ($emojiBtn, emoji) -> counter = $('.js-counter', $emojiBtn) - counterNumber = parseInt(counter.text()) + counterNumber = parseInt counter.text(), 10 if counterNumber > 1 - counter.text(counterNumber - 1) - @removeMeFromUserList($emojiBtn, emoji) + counter.text counterNumber - 1 + @removeMeFromUserList $emojiBtn, emoji else if emoji is 'thumbsup' or emoji is 'thumbsdown' - $emojiBtn.tooltip('destroy') - counter.text('0') - @removeMeFromUserList($emojiBtn, emoji) + $emojiBtn.tooltip 'destroy' + counter.text '0' + @removeMeFromUserList $emojiBtn, emoji @removeEmoji $emojiBtn if $emojiBtn.parents('.note').length else @removeEmoji $emojiBtn - $emojiBtn.removeClass('active') + $emojiBtn.removeClass 'active' removeEmoji: ($emojiBtn) -> @@ -207,9 +207,9 @@ class @AwardsHandler @resetTooltip(awardBlock) - addMeToUserList: (emoji) -> + addMeToUserList: (votesBlock, emoji) -> - awardBlock = @findEmojiIcon(emoji).parent() + awardBlock = @findEmojiIcon(votesBlock, emoji).parent() origTitle = @getAwardTooltip awardBlock users = [] @@ -231,29 +231,29 @@ class @AwardsHandler ), 200 - createEmoji_: (emoji) -> + createEmoji_: (votesBlock, emoji) -> emojiCssClass = @resolveNameToCssClass emoji - - buttonHtml = "" emoji_node = $(buttonHtml) - .insertBefore '.js-awards-block.current .js-award-holder:not(.js-award-action-btn)' + .insertBefore votesBlock.find '.js-award-holder:not(.js-award-action-btn)' .find '.emoji-icon' .data 'emoji', emoji $('.award-control').tooltip() - $('.js-awards-block.current').removeClass 'current' + votesBlock.removeClass 'current' - createEmoji: (emoji) -> + createEmoji: (votesBlock, emoji) -> - return @createEmoji_ emoji if $('.emoji-menu').length + if $('.emoji-menu').length + return @createEmoji_ votesBlock, emoji - @createEmojiMenu @getAwardMenuUrl(), => @createEmoji emoji + @createEmojiMenu @getAwardMenuUrl(), => @createEmoji votesBlock, emoji getAwardMenuUrl: -> return gl.awardMenuUrl or '/emojis' @@ -278,14 +278,14 @@ class @AwardsHandler callback.call() if data.ok - findEmojiIcon: (emoji) -> + findEmojiIcon: (votesBlock, emoji) -> - return $(".js-awards-block.current > .js-emoji-btn [data-emoji='#{emoji}']") + return votesBlock.find ".js-emoji-btn [data-emoji='#{emoji}']" scrollToAwards: -> - options = scrollTop: $('.awards').offset().top - 80 + options = scrollTop: $('.awards').offset().top - 110 $('body, html').animate options, 200 diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 101154114ba..0fdbe55ea9c 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -167,7 +167,8 @@ class @Notes return if note.award - awardsHandler.addAwardToEmojiBar(note.name) + votesBlock = $('.js-awards-block').eq 0 + awardsHandler.addAwardToEmojiBar votesBlock, note.name awardsHandler.scrollToAwards() # render note if it not present in loaded list -- cgit v1.2.1 From ce5729139fb8e37f4242996b29c4dfb181610acd Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Mon, 30 May 2016 16:15:02 +0300 Subject: Fix mutual exclusivity for emoji only comments. --- app/assets/javascripts/awards_handler.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 27b2853e7d1..2b3e6ad6224 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -131,7 +131,10 @@ class @AwardsHandler @createEmoji votesBlock, emoji - getVotesBlock: -> return $ '.js-awards-block.current' + getVotesBlock: -> + + currentBlock = $ '.js-awards-block.current' + return if currentBlock.length then currentBlock else $('.js-awards-block').eq 0 getAwardUrl: -> return @getVotesBlock().data 'award-url' -- cgit v1.2.1 From 59b34188fa26bd50e0696df6dd6fef786334f092 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Mon, 30 May 2016 16:27:25 +0300 Subject: Animate emoji when rendered. --- app/assets/javascripts/awards_handler.coffee | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 2b3e6ad6224..0c7b3493a19 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -126,6 +126,7 @@ class @AwardsHandler counter.text parseInt(counter.text()) + 1 $emojiBtn.addClass 'active' @addMeToUserList votesBlock, emoji + @animateEmoji $emojiBtn else votesBlock.removeClass 'hidden' @createEmoji votesBlock, emoji @@ -242,15 +243,25 @@ class @AwardsHandler 1 " - emoji_node = $(buttonHtml) + $emojiButton = $ buttonHtml + emoji_node = $emojiButton .insertBefore votesBlock.find '.js-award-holder:not(.js-award-action-btn)' .find '.emoji-icon' .data 'emoji', emoji + @animateEmoji $emojiButton $('.award-control').tooltip() votesBlock.removeClass 'current' + animateEmoji: ($emoji) -> + + className = 'pulse animated' + + $emoji.addClass className + setTimeout (-> $emoji.removeClass className), 321 + + createEmoji: (votesBlock, emoji) -> if $('.emoji-menu').length -- cgit v1.2.1 From 47fc235fe1d8b6387428a13ea7bda33b59561cee Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Mon, 30 May 2016 16:39:43 +0300 Subject: Be consistent on naming. It was named as $emojiBtn before I was using $emojiButton so updated them to be consistent. --- app/assets/javascripts/awards_handler.coffee | 44 ++++++++++++++-------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 0c7b3493a19..4425921b134 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -115,18 +115,18 @@ class @AwardsHandler @checkMutuality votesBlock, emoji if checkForMutuality @addEmojiToFrequentlyUsedList emoji - emoji = @normilizeEmojiName emoji - $emojiBtn = @findEmojiIcon(votesBlock, emoji).parent() + emoji = @normilizeEmojiName emoji + $emojiButton = @findEmojiIcon(votesBlock, emoji).parent() - if $emojiBtn.length > 0 - if @isActive $emojiBtn - @decrementCounter $emojiBtn, emoji + if $emojiButton.length > 0 + if @isActive $emojiButton + @decrementCounter $emojiButton, emoji else - counter = $emojiBtn.find '.js-counter' + counter = $emojiButton.find '.js-counter' counter.text parseInt(counter.text()) + 1 - $emojiBtn.addClass 'active' + $emojiButton.addClass 'active' @addMeToUserList votesBlock, emoji - @animateEmoji $emojiBtn + @animateEmoji $emojiButton else votesBlock.removeClass 'hidden' @createEmoji votesBlock, emoji @@ -153,32 +153,32 @@ class @AwardsHandler @addAward votesBlock, awardUrl, mutualVote, no if isAlreadyVoted - isActive: ($emojiBtn) -> $emojiBtn.hasClass 'active' + isActive: ($emojiButton) -> $emojiButton.hasClass 'active' - decrementCounter: ($emojiBtn, emoji) -> + decrementCounter: ($emojiButton, emoji) -> - counter = $('.js-counter', $emojiBtn) + counter = $('.js-counter', $emojiButton) counterNumber = parseInt counter.text(), 10 if counterNumber > 1 counter.text counterNumber - 1 - @removeMeFromUserList $emojiBtn, emoji + @removeMeFromUserList $emojiButton, emoji else if emoji is 'thumbsup' or emoji is 'thumbsdown' - $emojiBtn.tooltip 'destroy' + $emojiButton.tooltip 'destroy' counter.text '0' - @removeMeFromUserList $emojiBtn, emoji - @removeEmoji $emojiBtn if $emojiBtn.parents('.note').length + @removeMeFromUserList $emojiButton, emoji + @removeEmoji $emojiButton if $emojiButton.parents('.note').length else - @removeEmoji $emojiBtn + @removeEmoji $emojiButton - $emojiBtn.removeClass 'active' + $emojiButton.removeClass 'active' - removeEmoji: ($emojiBtn) -> + removeEmoji: ($emojiButton) -> - $emojiBtn.tooltip('destroy') - $emojiBtn.remove() + $emojiButton.tooltip('destroy') + $emojiButton.remove() $votesBlock = @getVotesBlock() @@ -191,9 +191,9 @@ class @AwardsHandler return $awardBlock.attr('data-original-title') or $awardBlock.attr('data-title') - removeMeFromUserList: ($emojiBtn, emoji) -> + removeMeFromUserList: ($emojiButton, emoji) -> - awardBlock = $emojiBtn + awardBlock = $emojiButton originalTitle = @getAwardTooltip awardBlock authors = originalTitle.split ', ' -- cgit v1.2.1 From f1d74ccc8ca8e3fa91e87193fc886e9a00a636b3 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Mon, 30 May 2016 19:33:43 +0300 Subject: Some design related tweaks. --- app/assets/javascripts/awards_handler.coffee | 2 +- app/assets/stylesheets/framework/timeline.scss | 2 +- app/assets/stylesheets/pages/awards.scss | 1 + app/assets/stylesheets/pages/notes.scss | 16 ++++++++++++++++ app/views/projects/issues/show.html.haml | 6 +++--- app/views/projects/notes/_note.html.haml | 4 ++-- 6 files changed, 24 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 4425921b134..8a6f6d7d185 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -39,7 +39,7 @@ class @AwardsHandler $menu = $ '.emoji-menu' - if $addBtn.hasClass 'note-emoji-button' + if $addBtn.hasClass 'js-note-emoji' $addBtn.parents('.note').find('.js-awards-block').addClass 'current' else $addBtn.closest('.js-awards-block').addClass 'current' diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss index 29501069d27..62935c95c51 100644 --- a/app/assets/stylesheets/framework/timeline.scss +++ b/app/assets/stylesheets/framework/timeline.scss @@ -5,7 +5,7 @@ padding: 0; .timeline-entry { - padding: $gl-padding $gl-btn-padding; + padding: $gl-padding $gl-btn-padding 11px $gl-btn-padding; border-color: $table-border-color; color: $gl-gray; border-bottom: 1px solid $border-white-light; diff --git a/app/assets/stylesheets/pages/awards.scss b/app/assets/stylesheets/pages/awards.scss index 07d40f40556..56d04229f0b 100644 --- a/app/assets/stylesheets/pages/awards.scss +++ b/app/assets/stylesheets/pages/awards.scss @@ -95,6 +95,7 @@ .award-control { margin-right: 5px; + margin-bottom: 5px; padding-left: 5px; padding-right: 5px; line-height: 20px; diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 3acc9152c01..53a9d1144c8 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -69,6 +69,10 @@ ul.notes { .note-edit-form { display: block; + + &.current-note-edit-form + .note-awards { + display: none; + } } } @@ -116,6 +120,18 @@ ul.notes { } } + .note-awards { + .js-awards-block { + padding: 2px 0; + margin-top: 10px; + } + + .award-control { + font-size: 13px; + padding: 2px 5px; + } + } + .note-header { padding-bottom: 3px; } diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index a35c13fbd40..b2f14a54073 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -68,9 +68,9 @@ #related-branches{ data: { url: related_branches_namespace_project_issue_url(@project.namespace, @project, @issue) } } // This element is filled in using JavaScript. - .content-block.content-block-small - = render 'new_branch' - = render 'award_emoji/awards_block', awardable: @issue, inline: true + .content-block.content-block-small + = render 'new_branch' + = render 'award_emoji/awards_block', awardable: @issue, inline: true %section.issuable-discussion = render 'projects/issues/discussion' diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 47ab8906316..3a1aa35fa21 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -22,7 +22,7 @@ %span.note-role = access - if note_editable - = link_to '#', title: 'Award Emoji', class: 'note-emoji-button js-add-award js-note-emoji', data: { position: 'right' } do + = link_to '#', title: 'Award Emoji', class: 'note-action-button note-emoji-button js-add-award js-note-emoji', data: { position: 'right' } do = icon('spinner spin') = icon('smile-o') = link_to '#', title: 'Edit comment', class: 'note-action-button js-note-edit' do @@ -33,11 +33,11 @@ .note-text = preserve do = markdown(note.note, pipeline: :note, cache_key: [note, "note"], author: note.author) + = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true) - if note_editable = render 'projects/notes/edit_form', note: note .note-awards = render 'award_emoji/awards_block', awardable: note, inline: false - = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true) - if note.attachment.url .note-attachment -- cgit v1.2.1 From 63a5402182aa9697bbd9e2d7192ef661c6d74b86 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Mon, 30 May 2016 20:04:08 +0300 Subject: Create and show emoji loader on the fly. --- app/assets/javascripts/awards_handler.coffee | 24 +++++++++++++++++++----- app/assets/stylesheets/pages/awards.scss | 3 ++- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 8a6f6d7d185..e01c5d68c43 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -100,12 +100,13 @@ class @AwardsHandler $menu.css(css) - addAward: (votesBlock, awardUrl, emoji, checkMutuality = yes) -> + addAward: (votesBlock, awardUrl, emoji, checkMutuality = yes, callback) -> emoji = @normilizeEmojiName emoji @postEmoji awardUrl, emoji, => @addAwardToEmojiBar votesBlock, emoji, checkMutuality + callback?() $('.emoji-menu').removeClass 'is-visible' @@ -146,11 +147,24 @@ class @AwardsHandler awardUrl = @getAwardUrl() if emoji in [ 'thumbsup', 'thumbsdown' ] - mutualVote = if emoji is 'thumbsup' then 'thumbsdown' else 'thumbsup' - selector = "[data-emoji=#{mutualVote}]" + mutualVote = if emoji is 'thumbsup' then 'thumbsdown' else 'thumbsup' + $emojiButton = votesBlock.find("[data-emoji=#{mutualVote}]").parent() + isAlreadyVoted = $emojiButton.hasClass 'active' - isAlreadyVoted = votesBlock.find(selector).parent().hasClass 'active' - @addAward votesBlock, awardUrl, mutualVote, no if isAlreadyVoted + if isAlreadyVoted + @showEmojiLoader $emojiButton + @addAward votesBlock, awardUrl, mutualVote, no, -> + $emojiButton.removeClass 'is-loading' + + + showEmojiLoader: ($emojiButton) -> + + $loader = $emojiButton.find '.fa-spinner' + + unless $loader.length + $emojiButton.append '' + + $emojiButton.addClass 'is-loading' isActive: ($emojiButton) -> $emojiButton.hasClass 'active' diff --git a/app/assets/stylesheets/pages/awards.scss b/app/assets/stylesheets/pages/awards.scss index 56d04229f0b..05d1ee5b998 100644 --- a/app/assets/stylesheets/pages/awards.scss +++ b/app/assets/stylesheets/pages/awards.scss @@ -109,7 +109,8 @@ } &.is-loading { - .award-control-icon-normal { + .award-control-icon-normal, + .emoji-icon { display: none; } -- cgit v1.2.1 From c1e2b02e34e5038be0c39e11c3172c3375dc0734 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Tue, 31 May 2016 22:32:29 +0300 Subject: Added tests for award emoji feature. --- app/assets/javascripts/awards_handler.coffee | 20 +- app/assets/stylesheets/pages/notes.scss | 2 +- spec/javascripts/awards_handler_spec.js.coffee | 202 +++++ .../behaviors/quick_submit_spec.js.coffee | 22 +- spec/javascripts/fixtures/awards_handler.html.haml | 52 ++ .../fixtures/behaviors/quick_submit.html.haml | 2 +- spec/javascripts/fixtures/emoji_menu.coffee | 957 +++++++++++++++++++++ spec/javascripts/new_branch_spec.js.coffee | 2 +- 8 files changed, 1235 insertions(+), 24 deletions(-) create mode 100644 spec/javascripts/awards_handler_spec.js.coffee create mode 100644 spec/javascripts/fixtures/awards_handler.html.haml create mode 100644 spec/javascripts/fixtures/emoji_menu.coffee diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index e01c5d68c43..4f4009e6db0 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -202,7 +202,7 @@ class @AwardsHandler getAwardTooltip: ($awardBlock) -> - return $awardBlock.attr('data-original-title') or $awardBlock.attr('data-title') + return $awardBlock.attr('data-original-title') or $awardBlock.attr('data-title') or '' removeMeFromUserList: ($emojiButton, emoji) -> @@ -222,7 +222,7 @@ class @AwardsHandler .attr 'data-original-title', newAuthors .attr 'data-title', newAuthors - @resetTooltip(awardBlock) + @resetTooltip awardBlock addMeToUserList: (votesBlock, emoji) -> @@ -232,21 +232,21 @@ class @AwardsHandler users = [] if origTitle - users = origTitle.trim().split(', ') + users = origTitle.trim().split ', ' - users.push('me') - awardBlock.attr('title', users.join(', ')) + users.push 'me' + awardBlock.attr 'title', users.join ', ' - @resetTooltip(awardBlock) + @resetTooltip awardBlock resetTooltip: (award) -> - award.tooltip('destroy') + + award.tooltip 'destroy' # 'destroy' call is asynchronous and there is no appropriate callback on it, this is why we need to set timeout. - setTimeout (-> - award.tooltip() - ), 200 + cb = -> award.tooltip() + setTimeout cb, 200 createEmoji_: (votesBlock, emoji) -> diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 53a9d1144c8..4ebaf227279 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -122,7 +122,7 @@ ul.notes { .note-awards { .js-awards-block { - padding: 2px 0; + padding: 2px; margin-top: 10px; } diff --git a/spec/javascripts/awards_handler_spec.js.coffee b/spec/javascripts/awards_handler_spec.js.coffee new file mode 100644 index 00000000000..5a95ae52dc8 --- /dev/null +++ b/spec/javascripts/awards_handler_spec.js.coffee @@ -0,0 +1,202 @@ +#= require awards_handler +#= require jquery +#= require jquery.cookie +#= require ./fixtures/emoji_menu + +awardsHandler = null +window.gl or= {} +window.gl.awardMenuUrl = '/emojis' +window.emojiAliases = -> return { '+1': 'thumbsup', '-1': 'thumbsdown' } + + +lazyAssert = (done, assertFn) -> + + setTimeout -> # Maybe jasmine.clock here? + assertFn() + done() + , 333 + + +describe 'AwardsHandler', -> + + fixture.preload 'awards_handler.html' + + beforeEach -> + fixture.load 'awards_handler.html' + awardsHandler = new AwardsHandler + spyOn(awardsHandler, 'postEmoji').and.callFake (url, emoji, cb) => cb() + spyOn(jQuery, 'get').and.callFake (req, cb) -> + expect(req).toBe '/emojis' + cb window.emojiMenu + + + describe '::showEmojiMenu', -> + + it 'should show emoji menu when Add emoji button clicked', (done) -> + + $('.js-add-award').eq(0).click() + + lazyAssert done, -> + $emojiMenu = $ '.emoji-menu' + expect($emojiMenu.length).toBe 1 + expect($emojiMenu.hasClass('is-visible')).toBe yes + expect($emojiMenu.find('#emoji_search').length).toBe 1 + expect($('.js-awards-block.current').length).toBe 1 + + + it 'should also show emoji menu for the smiley icon in notes', (done) -> + + $('.note-action-button').click() + + lazyAssert done, -> + $emojiMenu = $ '.emoji-menu' + expect($emojiMenu.length).toBe 1 + + + it 'should remove emoji menu when body is clicked', (done) -> + + $('.js-add-award').eq(0).click() + + lazyAssert done, -> + $emojiMenu = $('.emoji-menu') + $('body').click() + expect($emojiMenu.length).toBe 1 + expect($emojiMenu.hasClass('is-visible')).toBe no + expect($('.js-awards-block.current').length).toBe 0 + + + describe '::addAwardToEmojiBar', -> + + it 'should add emoji to votes block', -> + + $votesBlock = $('.js-awards-block').eq 0 + awardsHandler.addAwardToEmojiBar $votesBlock, 'heart', no + + $emojiButton = $votesBlock.find '[data-emoji=heart]' + + expect($emojiButton.length).toBe 1 + expect($emojiButton.next('.js-counter').text()).toBe '1' + expect($votesBlock.hasClass('hidden')).toBe no + + + it 'should remove the emoji when we click again', -> + + $votesBlock = $('.js-awards-block').eq 0 + awardsHandler.addAwardToEmojiBar $votesBlock, 'heart', no + awardsHandler.addAwardToEmojiBar $votesBlock, 'heart', no + $emojiButton = $votesBlock.find '[data-emoji=heart]' + + expect($emojiButton.length).toBe 0 + + + it 'should decrement the emoji counter', -> + + $votesBlock = $('.js-awards-block').eq 0 + awardsHandler.addAwardToEmojiBar $votesBlock, 'heart', no + + $emojiButton = $votesBlock.find '[data-emoji=heart]' + $emojiButton.next('.js-counter').text 5 + + awardsHandler.addAwardToEmojiBar $votesBlock, 'heart', no + + expect($emojiButton.length).toBe 1 + expect($emojiButton.next('.js-counter').text()).toBe '4' + + + describe '::getAwardUrl', -> + + it 'should return the url for request', -> + + expect(awardsHandler.getAwardUrl()).toBe '/gitlab-org/gitlab-test/issues/8/toggle_award_emoji' + + + describe '::addAward and ::checkMutuality', -> + + it 'should handle :+1: and :-1: mutuality', -> + + awardUrl = awardsHandler.getAwardUrl() + $votesBlock = $('.js-awards-block').eq 0 + $thumbsUpEmoji = $votesBlock.find('[data-emoji=thumbsup]').parent() + $thumbsDownEmoji = $votesBlock.find('[data-emoji=thumbsdown]').parent() + + awardsHandler.addAward $votesBlock, awardUrl, 'thumbsup', no + + expect($thumbsUpEmoji.hasClass('active')).toBe yes + expect($thumbsDownEmoji.hasClass('active')).toBe no + + $thumbsUpEmoji.tooltip() + $thumbsDownEmoji.tooltip() + + awardsHandler.addAward $votesBlock, awardUrl, 'thumbsdown', yes + + expect($thumbsUpEmoji.hasClass('active')).toBe no + expect($thumbsDownEmoji.hasClass('active')).toBe yes + + + describe '::removeEmoji', -> + + it 'should remove emoji', -> + + awardUrl = awardsHandler.getAwardUrl() + $votesBlock = $('.js-awards-block').eq 0 + + awardsHandler.addAward $votesBlock, awardUrl, 'fire', no + expect($votesBlock.find('[data-emoji=fire]').length).toBe 1 + + awardsHandler.removeEmoji $votesBlock.find('[data-emoji=fire]').closest('button') + expect($votesBlock.find('[data-emoji=fire]').length).toBe 0 + + + describe 'search', -> + + it 'should filter the emoji', -> + + $('.js-add-award').eq(0).click() + + expect($('[data-emoji=angel]').is(':visible')).toBe yes + expect($('[data-emoji=anger]').is(':visible')).toBe yes + + $('#emoji_search').val('ali').trigger 'keyup' + + expect($('[data-emoji=angel]').is(':visible')).toBe no + expect($('[data-emoji=anger]').is(':visible')).toBe no + expect($('[data-emoji=alien]').is(':visible')).toBe yes + expect($('h5.emoji-search').is(':visible')).toBe yes + + + describe 'emoji menu', -> + + selector = '[data-emoji=sunglasses]' + + openEmojiMenuAndAddEmoji = -> + + $('.js-add-award').eq(0).click() + + $menu = $ '.emoji-menu' + $block = $ '.js-awards-block' + $emoji = $menu.find ".emoji-menu-list-item #{selector}" + + expect($emoji.length).toBe 1 + expect($block.find(selector).length).toBe 0 + + $emoji.click() + + expect($menu.hasClass('.is-visible')).toBe no + expect($block.find(selector).length).toBe 1 + + + it 'should add selected emoji to awards block', -> + + openEmojiMenuAndAddEmoji() + + + it 'should remove already selected emoji', -> + + openEmojiMenuAndAddEmoji() + $('.js-add-award').eq(0).click() + + $block = $ '.js-awards-block' + $emoji = $('.emoji-menu').find ".emoji-menu-list-item #{selector}" + + $emoji.click() + expect($block.find(selector).length).toBe 0 diff --git a/spec/javascripts/behaviors/quick_submit_spec.js.coffee b/spec/javascripts/behaviors/quick_submit_spec.js.coffee index 09708c12ed4..d3b003a328a 100644 --- a/spec/javascripts/behaviors/quick_submit_spec.js.coffee +++ b/spec/javascripts/behaviors/quick_submit_spec.js.coffee @@ -14,17 +14,17 @@ describe 'Quick Submit behavior', -> } it 'does not respond to other keyCodes', -> - $('input').trigger(keydownEvent(keyCode: 32)) + $('input.quick-submit-input').trigger(keydownEvent(keyCode: 32)) expect(@spies.submit).not.toHaveBeenTriggered() it 'does not respond to Enter alone', -> - $('input').trigger(keydownEvent(ctrlKey: false, metaKey: false)) + $('input.quick-submit-input').trigger(keydownEvent(ctrlKey: false, metaKey: false)) expect(@spies.submit).not.toHaveBeenTriggered() it 'does not respond to repeated events', -> - $('input').trigger(keydownEvent(repeat: true)) + $('input.quick-submit-input').trigger(keydownEvent(repeat: true)) expect(@spies.submit).not.toHaveBeenTriggered() @@ -38,26 +38,26 @@ describe 'Quick Submit behavior', -> # only run the tests that apply to the current platform if navigator.userAgent.match(/Macintosh/) it 'responds to Meta+Enter', -> - $('input').trigger(keydownEvent()) + $('input.quick-submit-input').trigger(keydownEvent()) expect(@spies.submit).toHaveBeenTriggered() it 'excludes other modifier keys', -> - $('input').trigger(keydownEvent(altKey: true)) - $('input').trigger(keydownEvent(ctrlKey: true)) - $('input').trigger(keydownEvent(shiftKey: true)) + $('input.quick-submit-input').trigger(keydownEvent(altKey: true)) + $('input.quick-submit-input').trigger(keydownEvent(ctrlKey: true)) + $('input.quick-submit-input').trigger(keydownEvent(shiftKey: true)) expect(@spies.submit).not.toHaveBeenTriggered() else it 'responds to Ctrl+Enter', -> - $('input').trigger(keydownEvent()) + $('input.quick-submit-input').trigger(keydownEvent()) expect(@spies.submit).toHaveBeenTriggered() it 'excludes other modifier keys', -> - $('input').trigger(keydownEvent(altKey: true)) - $('input').trigger(keydownEvent(metaKey: true)) - $('input').trigger(keydownEvent(shiftKey: true)) + $('input.quick-submit-input').trigger(keydownEvent(altKey: true)) + $('input.quick-submit-input').trigger(keydownEvent(metaKey: true)) + $('input.quick-submit-input').trigger(keydownEvent(shiftKey: true)) expect(@spies.submit).not.toHaveBeenTriggered() diff --git a/spec/javascripts/fixtures/awards_handler.html.haml b/spec/javascripts/fixtures/awards_handler.html.haml new file mode 100644 index 00000000000..d55936ee4f9 --- /dev/null +++ b/spec/javascripts/fixtures/awards_handler.html.haml @@ -0,0 +1,52 @@ +.issue-details.issuable-details + .detail-page-description.content-block + %h2.title Quibusdam sint officiis earum molestiae ipsa autem voluptatem nisi rem. + .description.js-task-list-container.is-task-list-enabled + .wiki + %p Qui exercitationem magnam optio quae fuga earum odio. + %textarea.hidden.js-task-list-field Qui exercitationem magnam optio quae fuga earum odio. + %small.edited-text + .content-block.content-block-small + .awards.js-awards-block{"data-award-url" => "/gitlab-org/gitlab-test/issues/8/toggle_award_emoji"} + %button.award-control.btn.js-emoji-btn{"data-placement" => "bottom", "data-title" => "", :type => "button"} + .icon.emoji-icon.emoji-1F44D{"data-aliases" => "", "data-emoji" => "thumbsup", "data-unicode-name" => "1F44D", :title => "thumbsup"} + %span.award-control-text.js-counter 0 + %button.award-control.btn.js-emoji-btn{"data-placement" => "bottom", "data-title" => "", :type => "button"} + .icon.emoji-icon.emoji-1F44E{"data-aliases" => "", "data-emoji" => "thumbsdown", "data-unicode-name" => "1F44E", :title => "thumbsdown"} + %span.award-control-text.js-counter 0 + .award-menu-holder.js-award-holder + %button.btn.award-control.js-add-award{:type => "button"} + %i.fa.fa-smile-o.award-control-icon.award-control-icon-normal + %i.fa.fa-spinner.fa-spin.award-control-icon.award-control-icon-loading + %span.award-control-text Add + %section.issuable-discussion + #notes + %ul#notes-list.notes.main-notes-list.timeline + %li#note_348.note.note-row-348.timeline-entry{"data-author-id" => "18", "data-editable" => ""} + .timeline-entry-inner + .timeline-icon + %a{:href => "/u/agustin"} + %img.avatar.s40{:alt => "", :src => "#"}/ + .timeline-content + .note-header + %a.author_link{:href => "/u/agustin"} + %span.author Brenna Stokes + .inline.note-headline-light + @agustin commented + %a{:href => "#note_348"} + %time 11 days ago + .note-actions + %span.note-role Reporter + %a.note-action-button.note-emoji-button.js-add-award.js-note-emoji{"data-position" => "right", :href => "#", :title => "Award Emoji"} + %i.fa.fa-spinner.fa-spin + %i.fa.fa-smile-o + .js-task-list-container.note-body.is-task-list-enabled + .note-text + %p Suscipit sunt quia quisquam sed eveniet ipsam. + .note-awards + .awards.hidden.js-awards-block{"data-award-url" => "/gitlab-org/gitlab-test/notes/348/toggle_award_emoji"} + .award-menu-holder.js-award-holder + %button.btn.award-control.js-add-award{:type => "button"} + %i.fa.fa-smile-o.award-control-icon.award-control-icon-normal + %i.fa.fa-spinner.fa-spin.award-control-icon.award-control-icon-loading + %span.award-control-text Add diff --git a/spec/javascripts/fixtures/behaviors/quick_submit.html.haml b/spec/javascripts/fixtures/behaviors/quick_submit.html.haml index e3788bee813..dc2ceed42f4 100644 --- a/spec/javascripts/fixtures/behaviors/quick_submit.html.haml +++ b/spec/javascripts/fixtures/behaviors/quick_submit.html.haml @@ -1,5 +1,5 @@ %form.js-quick-submit{ action: '/foo' } - %input{ type: 'text' } + %input{ type: 'text', class: 'quick-submit-input'} %textarea %input{ type: 'submit'} Submit diff --git a/spec/javascripts/fixtures/emoji_menu.coffee b/spec/javascripts/fixtures/emoji_menu.coffee new file mode 100644 index 00000000000..e529dd5f1cd --- /dev/null +++ b/spec/javascripts/fixtures/emoji_menu.coffee @@ -0,0 +1,957 @@ +window.emojiMenu = """ +
    +
    + +
    + Emoticons +
    +
      +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    +
    +
    +""" diff --git a/spec/javascripts/new_branch_spec.js.coffee b/spec/javascripts/new_branch_spec.js.coffee index f2ce85efcdc..ce773793817 100644 --- a/spec/javascripts/new_branch_spec.js.coffee +++ b/spec/javascripts/new_branch_spec.js.coffee @@ -1,4 +1,4 @@ -#= require jquery-ui +#= require jquery-ui/autocomplete #= require new_branch_form describe 'Branch', -> -- cgit v1.2.1 From 5dbb3883f81682f07425c2ddb52bd0f75059885d Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Wed, 1 Jun 2016 20:14:45 +0300 Subject: Fix scss-lint. --- app/assets/stylesheets/framework/timeline.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss index 62935c95c51..0b0bd80c326 100644 --- a/app/assets/stylesheets/framework/timeline.scss +++ b/app/assets/stylesheets/framework/timeline.scss @@ -5,7 +5,7 @@ padding: 0; .timeline-entry { - padding: $gl-padding $gl-btn-padding 11px $gl-btn-padding; + padding: $gl-padding $gl-btn-padding 11px; border-color: $table-border-color; color: $gl-gray; border-bottom: 1px solid $border-white-light; -- cgit v1.2.1 From 7d3f8f542f0fe02ae27d5f527d578c19f47eec71 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Thu, 2 Jun 2016 18:25:51 +0200 Subject: Update tests on wording --- spec/controllers/projects/notes_controller_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index cb7d04a059c..00bc38b6071 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -16,12 +16,12 @@ describe Projects::NotesController do expect do post(:toggle_award_emoji, namespace_id: project.namespace.path, project_id: project.path, id: note.id, name: "thumbsup") - end.to change { AwardEmoji.count }.by(1) + end.to change { note.award_emoji.count }.by(1) expect(response.status).to eq(200) end - it "removes the already let award emoji" do + it "removes the already awarded emoji" do post(:toggle_award_emoji, namespace_id: project.namespace.path, project_id: project.path, id: note.id, name: "thumbsup") -- cgit v1.2.1 From 8dc1fa0d8dc9106ab95a3039723e9013c4eed00c Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Sat, 4 Jun 2016 00:16:13 +0300 Subject: Updated MR notes. --- app/assets/javascripts/awards_handler.coffee | 24 ++++++++++------------ app/assets/javascripts/dispatcher.js.coffee | 4 ++-- .../javascripts/lib/emoji_aliases.js.coffee.erb | 2 +- app/assets/javascripts/notes.js.coffee | 4 ++-- config/routes.rb | 1 + spec/javascripts/awards_handler_spec.js.coffee | 8 ++++---- 6 files changed, 21 insertions(+), 22 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 4f4009e6db0..efa8f6cd010 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -2,7 +2,7 @@ class @AwardsHandler constructor: -> - @aliases = emojiAliases() + @aliases = gl.emojiAliases() $(document) .off 'click', '.js-add-award' @@ -172,7 +172,7 @@ class @AwardsHandler decrementCounter: ($emojiButton, emoji) -> - counter = $('.js-counter', $emojiButton) + counter = $ '.js-counter', $emojiButton counterNumber = parseInt counter.text(), 10 if counterNumber > 1 @@ -218,9 +218,7 @@ class @AwardsHandler awardBlock .closest '.js-emoji-btn' .removeData 'original-title' - .removeData 'title' .attr 'data-original-title', newAuthors - .attr 'data-title', newAuthors @resetTooltip awardBlock @@ -258,8 +256,8 @@ class @AwardsHandler " $emojiButton = $ buttonHtml - emoji_node = $emojiButton - .insertBefore votesBlock.find '.js-award-holder:not(.js-award-action-btn)' + $emojiButton + .insertBefore votesBlock.find '.js-award-holder' .find '.emoji-icon' .data 'emoji', emoji @@ -281,21 +279,21 @@ class @AwardsHandler if $('.emoji-menu').length return @createEmoji_ votesBlock, emoji - @createEmojiMenu @getAwardMenuUrl(), => @createEmoji votesBlock, emoji + @createEmojiMenu @getAwardMenuUrl(), => @createEmoji_ votesBlock, emoji - getAwardMenuUrl: -> return gl.awardMenuUrl or '/emojis' + getAwardMenuUrl: -> return gl.awardMenuUrl resolveNameToCssClass: (emoji) -> - emoji_icon = $(".emoji-menu-content [data-emoji='#{emoji}']") + emojiIcon = $ ".emoji-menu-content [data-emoji='#{emoji}']" - if emoji_icon.length > 0 - unicodeName = emoji_icon.data('unicode-name') + if emojiIcon.length > 0 + unicodeName = emojiIcon.data 'unicode-name' else # Find by alias - unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data('unicode-name') + unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data 'unicode-name' return "emoji-#{unicodeName}" @@ -303,7 +301,7 @@ class @AwardsHandler postEmoji: (awardUrl, emoji, callback) -> $.post awardUrl, { name: emoji }, (data) -> - callback.call() if data.ok + callback() if data.ok findEmojiIcon: (votesBlock, emoji) -> diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index bae67a2ebaf..ec540060457 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -23,7 +23,7 @@ class Dispatcher new Issue() shortcut_handler = new ShortcutsIssuable() new ZenMode() - window.awardsHandler = new AwardsHandler() + gl.awardsHandler = new AwardsHandler() when 'projects:milestones:show', 'groups:milestones:show', 'dashboard:milestones:show' new Milestone() when 'dashboard:todos:index' @@ -54,7 +54,7 @@ class Dispatcher new Diff() shortcut_handler = new ShortcutsIssuable(true) new ZenMode() - window.awardsHandler = new AwardsHandler() + gl.awardsHandler = new AwardsHandler() when "projects:merge_requests:diffs" new Diff() new ZenMode() diff --git a/app/assets/javascripts/lib/emoji_aliases.js.coffee.erb b/app/assets/javascripts/lib/emoji_aliases.js.coffee.erb index 97be65116e2..80f9936b9c2 100644 --- a/app/assets/javascripts/lib/emoji_aliases.js.coffee.erb +++ b/app/assets/javascripts/lib/emoji_aliases.js.coffee.erb @@ -1,2 +1,2 @@ -window.emojiAliases = -> +gl.emojiAliases = -> JSON.parse('<%= Gitlab::AwardEmoji.aliases.to_json %>') diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 0fdbe55ea9c..8e33e915ba5 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -168,8 +168,8 @@ class @Notes if note.award votesBlock = $('.js-awards-block').eq 0 - awardsHandler.addAwardToEmojiBar votesBlock, note.name - awardsHandler.scrollToAwards() + gl.awardsHandler.addAwardToEmojiBar votesBlock, note.name + gl.awardsHandler.scrollToAwards() # render note if it not present in loaded list # or skip if rendered diff --git a/config/routes.rb b/config/routes.rb index 1fc7985136b..9b54b292845 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -757,6 +757,7 @@ Rails.application.routes.draw do resources :notes, only: [:index, :create, :destroy, :update], constraints: { id: /\d+/ } do member do + post :toggle_award_emoji delete :delete_attachment end end diff --git a/spec/javascripts/awards_handler_spec.js.coffee b/spec/javascripts/awards_handler_spec.js.coffee index 5a95ae52dc8..0bd6d696387 100644 --- a/spec/javascripts/awards_handler_spec.js.coffee +++ b/spec/javascripts/awards_handler_spec.js.coffee @@ -3,10 +3,10 @@ #= require jquery.cookie #= require ./fixtures/emoji_menu -awardsHandler = null -window.gl or= {} -window.gl.awardMenuUrl = '/emojis' -window.emojiAliases = -> return { '+1': 'thumbsup', '-1': 'thumbsdown' } +awardsHandler = null +window.gl or= {} +gl.emojiAliases = -> return { '+1': 'thumbsup', '-1': 'thumbsdown' } +gl.awardMenuUrl = '/emojis' lazyAssert = (done, assertFn) -> -- cgit v1.2.1