From 461d2672a495b7990dc578fa0bcba8bfdd781fd2 Mon Sep 17 00:00:00 2001 From: James Edwards-Jones Date: Tue, 14 Mar 2017 01:14:44 +0000 Subject: Set 'config.assets.compile = false' in production --- config/environments/production.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/environments/production.rb b/config/environments/production.rb index a9d8ac4b6d4..82a19085b1d 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -16,7 +16,7 @@ Rails.application.configure do # config.assets.css_compressor = :sass # Don't fallback to assets pipeline if a precompiled asset is missed - config.assets.compile = true + config.assets.compile = false # Generate digests for assets URLs config.assets.digest = true -- cgit v1.2.1 From d781d8daea087898b36e0961e4966cbf1c6a8ce6 Mon Sep 17 00:00:00 2001 From: Tim Zallmann Date: Wed, 26 Apr 2017 07:55:46 +0200 Subject: Replacing Base URL's if they have been entered into the profile --- app/views/users/show.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 03e5dd97405..075f78bca23 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -56,11 +56,11 @@ = icon('skype') - unless @user.linkedin.blank? .profile-link-holder.middle-dot-divider - = link_to "https://www.linkedin.com/in/#{@user.linkedin}", title: "LinkedIn" do + = link_to "https://www.linkedin.com/in/#{@user.linkedin.gsub(/https:\/\/www.linkedin.com\/in\//i,"")}", title: "LinkedIn" do = icon('linkedin-square') - unless @user.twitter.blank? .profile-link-holder.middle-dot-divider - = link_to "https://twitter.com/#{@user.twitter}", title: "Twitter" do + = link_to "https://twitter.com/#{@user.twitter.gsub(/https:\/\/twitter.com\//i,"")}", title: "Twitter" do = icon('twitter-square') - unless @user.website_url.blank? .profile-link-holder.middle-dot-divider -- cgit v1.2.1 From b7e41e9d792dab509a2e7c4b1b97a87860ba42ac Mon Sep 17 00:00:00 2001 From: Tim Zallmann Date: Wed, 26 Apr 2017 08:17:38 +0200 Subject: Doing the commit again with the correct E-Mail address setup --- app/views/users/show.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 075f78bca23..8f67c1d8f5f 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -56,11 +56,11 @@ = icon('skype') - unless @user.linkedin.blank? .profile-link-holder.middle-dot-divider - = link_to "https://www.linkedin.com/in/#{@user.linkedin.gsub(/https:\/\/www.linkedin.com\/in\//i,"")}", title: "LinkedIn" do + = link_to "https://www.linkedin.com/in/#{@user.linkedin.gsub(/https:\/\/www.linkedin.com\/in\//i,'')}", title: "LinkedIn" do = icon('linkedin-square') - unless @user.twitter.blank? .profile-link-holder.middle-dot-divider - = link_to "https://twitter.com/#{@user.twitter.gsub(/https:\/\/twitter.com\//i,"")}", title: "Twitter" do + = link_to "https://twitter.com/#{@user.twitter.gsub(/https:\/\/twitter.com\//i,'')}", title: "Twitter" do = icon('twitter-square') - unless @user.website_url.blank? .profile-link-holder.middle-dot-divider -- cgit v1.2.1 From 7e7a896d9c6509fabd1fea09a835b5d7a86dcf9a Mon Sep 17 00:00:00 2001 From: Tim Zallmann Date: Wed, 26 Apr 2017 07:55:46 +0200 Subject: Replacing Base URL's if they have been entered into the profile --- app/views/users/show.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 03e5dd97405..075f78bca23 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -56,11 +56,11 @@ = icon('skype') - unless @user.linkedin.blank? .profile-link-holder.middle-dot-divider - = link_to "https://www.linkedin.com/in/#{@user.linkedin}", title: "LinkedIn" do + = link_to "https://www.linkedin.com/in/#{@user.linkedin.gsub(/https:\/\/www.linkedin.com\/in\//i,"")}", title: "LinkedIn" do = icon('linkedin-square') - unless @user.twitter.blank? .profile-link-holder.middle-dot-divider - = link_to "https://twitter.com/#{@user.twitter}", title: "Twitter" do + = link_to "https://twitter.com/#{@user.twitter.gsub(/https:\/\/twitter.com\//i,"")}", title: "Twitter" do = icon('twitter-square') - unless @user.website_url.blank? .profile-link-holder.middle-dot-divider -- cgit v1.2.1 From 20129fd65f397d8376321960764380da0a3e0823 Mon Sep 17 00:00:00 2001 From: Tim Zallmann Date: Wed, 26 Apr 2017 08:17:38 +0200 Subject: Doing the commit again with the correct E-Mail address setup --- app/views/users/show.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 075f78bca23..8f67c1d8f5f 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -56,11 +56,11 @@ = icon('skype') - unless @user.linkedin.blank? .profile-link-holder.middle-dot-divider - = link_to "https://www.linkedin.com/in/#{@user.linkedin.gsub(/https:\/\/www.linkedin.com\/in\//i,"")}", title: "LinkedIn" do + = link_to "https://www.linkedin.com/in/#{@user.linkedin.gsub(/https:\/\/www.linkedin.com\/in\//i,'')}", title: "LinkedIn" do = icon('linkedin-square') - unless @user.twitter.blank? .profile-link-holder.middle-dot-divider - = link_to "https://twitter.com/#{@user.twitter.gsub(/https:\/\/twitter.com\//i,"")}", title: "Twitter" do + = link_to "https://twitter.com/#{@user.twitter.gsub(/https:\/\/twitter.com\//i,'')}", title: "Twitter" do = icon('twitter-square') - unless @user.website_url.blank? .profile-link-holder.middle-dot-divider -- cgit v1.2.1 From d6697e0b4d5d9c1437b57b2307464c93d3c66c66 Mon Sep 17 00:00:00 2001 From: Tim Zallmann Date: Mon, 8 May 2017 15:23:22 +0200 Subject: Created Application Helpers based on suggestion for easier parsing --- app/helpers/application_helper.rb | 19 +++++++++++++++++++ app/views/users/show.html.haml | 4 ++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 6d6bcbaf88a..03bc773fa2a 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -278,4 +278,23 @@ module ApplicationHelper def show_user_callout? cookies[:user_callout_dismissed] == 'true' end + + def linkedin_url(user) + name = user.linkedin + if name =~ %r{\Ahttps?:\/\/(www\.)?linkedin\.com\/in\/(.*)\z} + name + else + "https://www.linkedin.com/in/#{name}" + end + end + + def twitter_url(user) + name = user.twitter + if name =~ %r{\Ahttps?:\/\/(www\.)?twitter\.com\/(.*)\z} + name + else + "https://www.twitter.com/#{name}" + end + end + end diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 8f67c1d8f5f..8e8b84e0408 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -56,11 +56,11 @@ = icon('skype') - unless @user.linkedin.blank? .profile-link-holder.middle-dot-divider - = link_to "https://www.linkedin.com/in/#{@user.linkedin.gsub(/https:\/\/www.linkedin.com\/in\//i,'')}", title: "LinkedIn" do + = link_to linkedin_url(@user), title: "LinkedIn" do = icon('linkedin-square') - unless @user.twitter.blank? .profile-link-holder.middle-dot-divider - = link_to "https://twitter.com/#{@user.twitter.gsub(/https:\/\/twitter.com\//i,'')}", title: "Twitter" do + = link_to twitter_url(@user), title: "Twitter" do = icon('twitter-square') - unless @user.website_url.blank? .profile-link-holder.middle-dot-divider -- cgit v1.2.1 From 1491ed656e528d475326a53aa4d69f6577aba6b6 Mon Sep 17 00:00:00 2001 From: Tim Zallmann Date: Mon, 8 May 2017 16:45:59 +0200 Subject: Fixing Static Analysis Fail with Indentation --- app/helpers/application_helper.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 03bc773fa2a..ce9413172a5 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -288,7 +288,7 @@ module ApplicationHelper end end - def twitter_url(user) + def twitter_url(user) name = user.twitter if name =~ %r{\Ahttps?:\/\/(www\.)?twitter\.com\/(.*)\z} name @@ -296,5 +296,4 @@ module ApplicationHelper "https://www.twitter.com/#{name}" end end - end -- cgit v1.2.1 From dbbe15e69103e50bca0c6ccf5f94b4871ede7ab7 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Mon, 8 May 2017 16:42:38 -0500 Subject: Automatically close user dropdown for CE --- app/assets/javascripts/users_select.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index 38462782007..6b70615c2ef 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -422,6 +422,15 @@ window.emitSidebarEvent = window.emitSidebarEvent || $.noop; selected = $dropdown.closest('.selectbox').find("input[name='" + ($dropdown.data('field-name')) + "']").val(); return assignTo(selected); } + + // Automatically close dropdown after assignee is selected + // since CE has no multiple assignees + if (getSelected().length === $dropdown.data('max-select')) { + // Close the dropdown + this.el.click(); + + this.hidden(); + } }, id: function (user) { return user.id; -- cgit v1.2.1 From a72bc1031b91c7de88711b0fc94cd6e8895ead30 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Mon, 8 May 2017 17:13:11 -0500 Subject: Add spec --- spec/features/issues/form_spec.rb | 16 ++++++++++++++++ spec/features/issues/issue_sidebar_spec.rb | 10 ++++++++++ 2 files changed, 26 insertions(+) diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb index 87adce3cddd..7d13690f284 100644 --- a/spec/features/issues/form_spec.rb +++ b/spec/features/issues/form_spec.rb @@ -23,6 +23,22 @@ describe 'New/edit issue', feature: true, js: true do visit new_namespace_project_issue_path(project.namespace, project) end + describe 'single assignee' do + it 'hides assignee after selection' do + click_button 'Unassigned' + + page.within('.dropdown-menu-user') do + click_link user.name + end + + # TODO: Figure out a better way to wait for dropdown CSS hide + # animation to complete + sleep 0.3 + + expect(page.find('.dropdown-menu-user', visible: false)).not_to be_visible + end + end + describe 'multiple assignees' do before do click_button 'Unassigned' diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb index 0de0f93089a..dd8c46f1044 100644 --- a/spec/features/issues/issue_sidebar_spec.rb +++ b/spec/features/issues/issue_sidebar_spec.rb @@ -57,6 +57,16 @@ feature 'Issue Sidebar', feature: true do expect(page.find('.dropdown-menu-user-link.is-active')).to have_content(user.name) end end + + context 'single assignee' do + it 'hides assignee after selection' do + page.within('.dropdown-menu-user') do + click_link user.name + end + + expect(page.find('.block.assignee .selectbox', visible: false)).not_to be_visible + end + end end context 'as a allowed user' do -- cgit v1.2.1 From 0b06ba0b06982e4f318c7a72b4f91ac6158e673b Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Tue, 9 May 2017 11:11:53 -0500 Subject: Fix issue spec --- spec/features/issues_spec.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 5285dda361b..396a923082d 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -465,8 +465,6 @@ describe 'Issues', feature: true do click_link 'Edit' click_link @user.name - find('.dropdown-menu-toggle').click - page.within '.value .author' do expect(page).to have_content @user.name end @@ -474,8 +472,6 @@ describe 'Issues', feature: true do click_link 'Edit' click_link @user.name - find('.dropdown-menu-toggle').click - page.within '.value .assign-yourself' do expect(page).to have_content "No assignee" end -- cgit v1.2.1 From f154a9ecec2aaf35a10cff3d6131e5f93523c95f Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Tue, 9 May 2017 14:14:14 -0500 Subject: Fix sidebar spec --- spec/features/boards/sidebar_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb index 7c53d2b47d9..a5ef280a60f 100644 --- a/spec/features/boards/sidebar_spec.rb +++ b/spec/features/boards/sidebar_spec.rb @@ -115,7 +115,6 @@ describe 'Issue Boards', feature: true, js: true do click_link 'Unassigned' end - find('.dropdown-menu-toggle').click wait_for_vue_resource expect(page).to have_content('No assignee') -- cgit v1.2.1 From 3e372ccab26d991454289e9d76c4bfe20ed2d789 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Wed, 10 May 2017 12:37:36 -0500 Subject: Remove extra pipeline margin --- app/assets/stylesheets/pages/pipelines.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index e7553c7a4bf..6d189682eb7 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -191,7 +191,6 @@ } .commit-title { - margin-top: 4px; max-width: 225px; overflow: hidden; white-space: nowrap; @@ -224,7 +223,7 @@ .duration, .finished-at { color: $gl-text-color-secondary; - margin: 4px 0; + margin: 0; white-space: nowrap; .fa { -- cgit v1.2.1 From 835d4487fb7a67ff0f0254ee4120bcdf12be5a07 Mon Sep 17 00:00:00 2001 From: Tim Zallmann Date: Thu, 11 May 2017 09:10:29 +0200 Subject: Fixed Regex(es) --- app/helpers/application_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index ce9413172a5..1bca588fdc9 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -281,7 +281,7 @@ module ApplicationHelper def linkedin_url(user) name = user.linkedin - if name =~ %r{\Ahttps?:\/\/(www\.)?linkedin\.com\/in\/(.*)\z} + if name =~ %r{\Ahttps?:\/\/(www\.)?linkedin\.com\/in\/} name else "https://www.linkedin.com/in/#{name}" @@ -290,7 +290,7 @@ module ApplicationHelper def twitter_url(user) name = user.twitter - if name =~ %r{\Ahttps?:\/\/(www\.)?twitter\.com\/(.*)\z} + if name =~ %r{\Ahttps?:\/\/(www\.)?twitter\.com\/} name else "https://www.twitter.com/#{name}" -- cgit v1.2.1 From be646ba0759b86686608faff6ff8b656922e7107 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Thu, 11 May 2017 12:09:12 -0500 Subject: Add helper to disable css animation in rspec --- spec/disable_css_animation.js | 15 ++++++++++++++ spec/disable_css_animation_helper.rb | 9 +++++++++ spec/features/issues/form_spec.rb | 39 ++---------------------------------- 3 files changed, 26 insertions(+), 37 deletions(-) create mode 100644 spec/disable_css_animation.js create mode 100644 spec/disable_css_animation_helper.rb diff --git a/spec/disable_css_animation.js b/spec/disable_css_animation.js new file mode 100644 index 00000000000..b26676d2e98 --- /dev/null +++ b/spec/disable_css_animation.js @@ -0,0 +1,15 @@ +/* eslint-disable */ +// Disable eslint because capybara does not know es6 + +var disableAnimationStyles = '-webkit-transition: none !important;' + + '-moz-transition: none !important;' + + '-ms-transition: none !important;' + + '-o-transition: none !important;' + + 'transition: none !important;' + +window.onload = function() { + var animationStyles = document.createElement('style'); + animationStyles.type = 'text/css'; + animationStyles.innerHTML = '* {' + disableAnimationStyles + '}'; + document.head.appendChild(animationStyles); +}; diff --git a/spec/disable_css_animation_helper.rb b/spec/disable_css_animation_helper.rb new file mode 100644 index 00000000000..1cc69ff55c8 --- /dev/null +++ b/spec/disable_css_animation_helper.rb @@ -0,0 +1,9 @@ +Capybara.register_driver :poltergeist do |app| + opts = { + extensions: ["#{Rails.root}/spec/disable_css_animation.js"] # or wherever + } + + Capybara::Poltergeist::Driver.new(app, opts) +end + +Capybara.javascript_driver = :poltergeist diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb index 7d13690f284..a12a946b1bb 100644 --- a/spec/features/issues/form_spec.rb +++ b/spec/features/issues/form_spec.rb @@ -1,4 +1,5 @@ require 'rails_helper' +require 'disable_css_animation_helper' describe 'New/edit issue', feature: true, js: true do include GitlabRoutingHelper @@ -33,48 +34,12 @@ describe 'New/edit issue', feature: true, js: true do # TODO: Figure out a better way to wait for dropdown CSS hide # animation to complete - sleep 0.3 + # sleep 0.3 expect(page.find('.dropdown-menu-user', visible: false)).not_to be_visible end end - describe 'multiple assignees' do - before do - click_button 'Unassigned' - end - - it 'unselects other assignees when unassigned is selected' do - page.within '.dropdown-menu-user' do - click_link user2.name - end - - page.within '.dropdown-menu-user' do - click_link 'Unassigned' - end - - page.within '.js-assignee-search' do - expect(page).to have_content 'Unassigned' - end - - expect(find('input[name="issue[assignee_ids][]"]', visible: false).value).to match('0') - end - - it 'toggles assign to me when current user is selected and unselected' do - page.within '.dropdown-menu-user' do - click_link user.name - end - - expect(find('a', text: 'Assign to me', visible: false)).not_to be_visible - - page.within '.dropdown-menu-user' do - click_link user.name - end - - expect(find('a', text: 'Assign to me')).to be_visible - end - end - it 'allows user to create new issue' do fill_in 'issue_title', with: 'title' fill_in 'issue_description', with: 'title' -- cgit v1.2.1 From 6d46e51031e3d27044f692eba2fe332f26f59478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Rodr=C3=ADguez?= Date: Thu, 11 May 2017 16:48:59 -0300 Subject: Use gitaly_migrate helper on all current migrations in repository --- lib/gitlab/git/repository.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 256318cb833..639240b83cc 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -114,7 +114,7 @@ module Gitlab # Returns the number of valid branches def branch_count - Gitlab::GitalyClient.migrate(:branch_names) do |is_enabled| + gitaly_migrate(:branch_names) do |is_enabled| if is_enabled gitaly_ref_client.count_branch_names else @@ -133,7 +133,7 @@ module Gitlab # Returns the number of valid tags def tag_count - Gitlab::GitalyClient.migrate(:tag_names) do |is_enabled| + gitaly_migrate(:tag_names) do |is_enabled| if is_enabled gitaly_ref_client.count_tag_names else @@ -471,7 +471,7 @@ module Gitlab def ref_name_for_sha(ref_path, sha) # NOTE: This feature is intentionally disabled until # https://gitlab.com/gitlab-org/gitaly/issues/180 is resolved - # Gitlab::GitalyClient.migrate(:find_ref_name) do |is_enabled| + # gitaly_migrate(:find_ref_name) do |is_enabled| # if is_enabled # gitaly_ref_client.find_ref_name(sha, ref_path) # else -- cgit v1.2.1 From c5e4e676e3b1dc5aa0b71fd9c026c0e172cda808 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 11 May 2017 17:29:42 -0300 Subject: Fix token interpolation when setting the Github remote --- lib/github/import.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/github/import.rb b/lib/github/import.rb index 06beb607a3e..452aebc66cb 100644 --- a/lib/github/import.rb +++ b/lib/github/import.rb @@ -1,4 +1,5 @@ require_relative 'error' + module Github class Import include Gitlab::ShellAdapter @@ -79,7 +80,7 @@ module Github def fetch_repository begin project.create_repository unless project.repository.exists? - project.repository.add_remote('github', "https://{options.fetch(:token)}@github.com/#{repo}.git") + project.repository.add_remote('github', "https://#{options.fetch(:token)}@github.com/#{repo}.git") project.repository.set_remote_as_mirror('github') project.repository.fetch_remote('github', forced: true) rescue Gitlab::Shell::Error => e -- cgit v1.2.1 From e901d1209e77e2ff0d85bb14c4a8df6da135b333 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 11 May 2017 17:30:56 -0300 Subject: Reset create callbacks for Issues/MRs while importing GitHub projects --- lib/github/import.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/github/import.rb b/lib/github/import.rb index 452aebc66cb..9c7eb965f93 100644 --- a/lib/github/import.rb +++ b/lib/github/import.rb @@ -7,6 +7,7 @@ module Github class MergeRequest < ::MergeRequest self.table_name = 'merge_requests' + self.reset_callbacks :create self.reset_callbacks :save self.reset_callbacks :commit self.reset_callbacks :update @@ -17,6 +18,7 @@ module Github self.table_name = 'issues' self.reset_callbacks :save + self.reset_callbacks :create self.reset_callbacks :commit self.reset_callbacks :update self.reset_callbacks :validate -- cgit v1.2.1 From 851a8b1f739d045076767497db5f89d99034183e Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 11 May 2017 17:33:16 -0300 Subject: Add CHANGELOG --- changelogs/unreleased/fix-github-import.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/fix-github-import.yml diff --git a/changelogs/unreleased/fix-github-import.yml b/changelogs/unreleased/fix-github-import.yml new file mode 100644 index 00000000000..3a57152f7a8 --- /dev/null +++ b/changelogs/unreleased/fix-github-import.yml @@ -0,0 +1,4 @@ +--- +title: Fix token interpolation when setting the Github remote +merge_request: +author: -- cgit v1.2.1 From 9740f6a77a1fb7b24f8a9f319fc5deeb7cfe5ca2 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Thu, 11 May 2017 16:20:20 -0500 Subject: Add additional EE check --- app/assets/javascripts/users_select.js | 4 +++- spec/features/issues/form_spec.rb | 4 ---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index 6b70615c2ef..99f4de1efb8 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -425,7 +425,9 @@ window.emitSidebarEvent = window.emitSidebarEvent || $.noop; // Automatically close dropdown after assignee is selected // since CE has no multiple assignees - if (getSelected().length === $dropdown.data('max-select')) { + // EE does not have a max-select + if ($dropdown.data('max-select') && + getSelected().length === $dropdown.data('max-select')) { // Close the dropdown this.el.click(); diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb index a12a946b1bb..12a89ad723f 100644 --- a/spec/features/issues/form_spec.rb +++ b/spec/features/issues/form_spec.rb @@ -32,10 +32,6 @@ describe 'New/edit issue', feature: true, js: true do click_link user.name end - # TODO: Figure out a better way to wait for dropdown CSS hide - # animation to complete - # sleep 0.3 - expect(page.find('.dropdown-menu-user', visible: false)).not_to be_visible end end -- cgit v1.2.1 From 477be05661d2cd3a550510f898d8d1d222d1598f Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 12 May 2017 09:27:21 +0100 Subject: Fixed issue boards not filtering when URL params are encoded Closes #32084 --- app/assets/javascripts/lib/utils/common_utils.js | 2 +- spec/javascripts/lib/utils/common_utils_spec.js | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 2f682fbd2fb..2eab6f975bb 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -135,7 +135,7 @@ gl.utils.getUrlParamsArray = function () { // We can trust that each param has one & since values containing & will be encoded // Remove the first character of search as it is always ? - return window.location.search.slice(1).split('&'); + return window.location.search.slice(1).split('&').map(param => decodeURI(param)); }; gl.utils.isMetaKey = function(e) { diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js index 5eb147ed888..42a9067ade5 100644 --- a/spec/javascripts/lib/utils/common_utils_spec.js +++ b/spec/javascripts/lib/utils/common_utils_spec.js @@ -41,6 +41,16 @@ require('~/lib/utils/common_utils'); const paramsArray = gl.utils.getUrlParamsArray(); expect(paramsArray[0][0] !== '?').toBe(true); }); + + it('should decode params', () => { + history.pushState('', '', '?label_name%5B%5D=test'); + + expect( + gl.utils.getUrlParamsArray()[0], + ).toBe('label_name[]=test'); + + history.pushState('', '', '?'); + }); }); describe('gl.utils.handleLocationHash', () => { -- cgit v1.2.1 From 9923a630af7990c7fccc39f3e6584fb6b8bb2267 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Fri, 12 May 2017 09:58:22 -0500 Subject: Use fa-refresh on retried jobs --- app/assets/stylesheets/pages/builds.scss | 2 +- app/views/projects/builds/_sidebar.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index 724b4080ee0..14a62b6cbf0 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -378,7 +378,7 @@ background-color: $row-hover; } - .fa-spinner { + .fa-refresh { font-size: 13px; margin-left: 3px; } diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml index 43191fae9e6..26c892d0fd2 100644 --- a/app/views/projects/builds/_sidebar.html.haml +++ b/app/views/projects/builds/_sidebar.html.haml @@ -136,7 +136,7 @@ - else = build.id - if build.retried? - %i.fa.fa-spinner.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' } + %i.fa.fa-refresh.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' } :javascript new Sidebar(); -- cgit v1.2.1 From d567b6474ab0b2560878d76ae0c53a41ab8dba4f Mon Sep 17 00:00:00 2001 From: tauriedavis Date: Fri, 12 May 2017 10:08:50 -0700 Subject: 31886 Remove spinner from loading comment --- app/assets/javascripts/notes.js | 3 --- changelogs/unreleased/31886-remover-comment-load-spinner.yml | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/31886-remover-comment-load-spinner.yml diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index 7ba44835741..bce5379cbb9 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -1183,9 +1183,6 @@ const normalizeNewlines = function(str) { @${currentUsername} - - -
diff --git a/changelogs/unreleased/31886-remover-comment-load-spinner.yml b/changelogs/unreleased/31886-remover-comment-load-spinner.yml new file mode 100644 index 00000000000..4b36538064a --- /dev/null +++ b/changelogs/unreleased/31886-remover-comment-load-spinner.yml @@ -0,0 +1,4 @@ +--- +title: Remove spinner from loading comment +merge_request: +author: -- cgit v1.2.1 From 28ebd70641493d9628961e1833bbb62be5cd36df Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Fri, 12 May 2017 12:59:08 -0500 Subject: Remove unnecessary comment --- spec/disable_css_animation_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/disable_css_animation_helper.rb b/spec/disable_css_animation_helper.rb index 1cc69ff55c8..ef3967c8fa6 100644 --- a/spec/disable_css_animation_helper.rb +++ b/spec/disable_css_animation_helper.rb @@ -1,6 +1,6 @@ Capybara.register_driver :poltergeist do |app| opts = { - extensions: ["#{Rails.root}/spec/disable_css_animation.js"] # or wherever + extensions: ["#{Rails.root}/spec/disable_css_animation.js"] } Capybara::Poltergeist::Driver.new(app, opts) -- cgit v1.2.1 From 2c5e70bec63aa707e5c91b9a7fadbb42de41e983 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Fri, 12 May 2017 13:04:07 -0500 Subject: Fix conflict on users_select --- app/assets/javascripts/users_select.js | 1205 ++++++++++++++++---------------- 1 file changed, 599 insertions(+), 606 deletions(-) diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index 99f4de1efb8..24c95f9a14c 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -5,667 +5,660 @@ // TODO: remove eventHub hack after code splitting refactor window.emitSidebarEvent = window.emitSidebarEvent || $.noop; -(function() { - var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; }, - slice = [].slice; - - this.UsersSelect = (function() { - function UsersSelect(currentUser, els) { - var $els; - this.users = bind(this.users, this); - this.user = bind(this.user, this); - this.usersPath = "/autocomplete/users.json"; - this.userPath = "/autocomplete/users/:id.json"; - if (currentUser != null) { - if (typeof currentUser === 'object') { - this.currentUser = currentUser; - } else { - this.currentUser = JSON.parse(currentUser); +function UsersSelect(currentUser, els) { + var $els; + this.users = this.users.bind(this); + this.user = this.user.bind(this); + this.usersPath = "/autocomplete/users.json"; + this.userPath = "/autocomplete/users/:id.json"; + if (currentUser != null) { + if (typeof currentUser === 'object') { + this.currentUser = currentUser; + } else { + this.currentUser = JSON.parse(currentUser); + } + } + + $els = $(els); + + if (!els) { + $els = $('.js-user-search'); + } + + $els.each((function(_this) { + return function(i, dropdown) { + var options = {}; + var $block, $collapsedSidebar, $dropdown, $loading, $selectbox, $value, abilityName, assignTo, assigneeTemplate, collapsedAssigneeTemplate, defaultLabel, defaultNullUser, firstUser, issueURL, selectedId, selectedIdDefault, showAnyUser, showNullUser, showMenuAbove; + $dropdown = $(dropdown); + options.projectId = $dropdown.data('project-id'); + options.groupId = $dropdown.data('group-id'); + options.showCurrentUser = $dropdown.data('current-user'); + options.todoFilter = $dropdown.data('todo-filter'); + options.todoStateFilter = $dropdown.data('todo-state-filter'); + showNullUser = $dropdown.data('null-user'); + defaultNullUser = $dropdown.data('null-user-default'); + showMenuAbove = $dropdown.data('showMenuAbove'); + showAnyUser = $dropdown.data('any-user'); + firstUser = $dropdown.data('first-user'); + options.authorId = $dropdown.data('author-id'); + defaultLabel = $dropdown.data('default-label'); + issueURL = $dropdown.data('issueUpdate'); + $selectbox = $dropdown.closest('.selectbox'); + $block = $selectbox.closest('.block'); + abilityName = $dropdown.data('ability-name'); + $value = $block.find('.value'); + $collapsedSidebar = $block.find('.sidebar-collapsed-user'); + $loading = $block.find('.block-loading').fadeOut(); + selectedIdDefault = (defaultNullUser && showNullUser) ? 0 : null; + selectedId = $dropdown.data('selected') || selectedIdDefault; + + const assignYourself = function () { + const unassignedSelected = $dropdown.closest('.selectbox') + .find(`input[name='${$dropdown.data('field-name')}'][value=0]`); + + if (unassignedSelected) { + unassignedSelected.remove(); } - } - $els = $(els); + // Save current selected user to the DOM + const input = document.createElement('input'); + input.type = 'hidden'; + input.name = $dropdown.data('field-name'); + + const currentUserInfo = $dropdown.data('currentUserInfo'); + + if (currentUserInfo) { + input.value = currentUserInfo.id; + input.dataset.meta = currentUserInfo.name; + } else if (_this.currentUser) { + input.value = _this.currentUser.id; + } - if (!els) { - $els = $('.js-user-search'); + if ($selectbox) { + $dropdown.parent().before(input); + } else { + $dropdown.after(input); + } + }; + + if ($block[0]) { + $block[0].addEventListener('assignYourself', assignYourself); } - $els.each((function(_this) { - return function(i, dropdown) { - var options = {}; - var $block, $collapsedSidebar, $dropdown, $loading, $selectbox, $value, abilityName, assignTo, assigneeTemplate, collapsedAssigneeTemplate, defaultLabel, defaultNullUser, firstUser, issueURL, selectedId, selectedIdDefault, showAnyUser, showNullUser, showMenuAbove; - $dropdown = $(dropdown); - options.projectId = $dropdown.data('project-id'); - options.groupId = $dropdown.data('group-id'); - options.showCurrentUser = $dropdown.data('current-user'); - options.todoFilter = $dropdown.data('todo-filter'); - options.todoStateFilter = $dropdown.data('todo-state-filter'); - showNullUser = $dropdown.data('null-user'); - defaultNullUser = $dropdown.data('null-user-default'); - showMenuAbove = $dropdown.data('showMenuAbove'); - showAnyUser = $dropdown.data('any-user'); - firstUser = $dropdown.data('first-user'); - options.authorId = $dropdown.data('author-id'); - defaultLabel = $dropdown.data('default-label'); - issueURL = $dropdown.data('issueUpdate'); - $selectbox = $dropdown.closest('.selectbox'); - $block = $selectbox.closest('.block'); - abilityName = $dropdown.data('ability-name'); - $value = $block.find('.value'); - $collapsedSidebar = $block.find('.sidebar-collapsed-user'); - $loading = $block.find('.block-loading').fadeOut(); - selectedIdDefault = (defaultNullUser && showNullUser) ? 0 : null; - selectedId = $dropdown.data('selected') || selectedIdDefault; - - const assignYourself = function () { - const unassignedSelected = $dropdown.closest('.selectbox') - .find(`input[name='${$dropdown.data('field-name')}'][value=0]`); - - if (unassignedSelected) { - unassignedSelected.remove(); - } + const getSelectedUserInputs = function() { + return $selectbox + .find(`input[name="${$dropdown.data('field-name')}"]`); + }; + + const getSelected = function() { + return getSelectedUserInputs() + .map((index, input) => parseInt(input.value, 10)) + .get(); + }; + + const checkMaxSelect = function() { + const maxSelect = $dropdown.data('max-select'); + if (maxSelect) { + const selected = getSelected(); + + if (selected.length > maxSelect) { + const firstSelectedId = selected[0]; + const firstSelected = $dropdown.closest('.selectbox') + .find(`input[name='${$dropdown.data('field-name')}'][value=${firstSelectedId}]`); + + firstSelected.remove(); + emitSidebarEvent('sidebar.removeAssignee', { + id: firstSelectedId, + }); + } + } + }; + + const getMultiSelectDropdownTitle = function(selectedUser, isSelected) { + const selectedUsers = getSelected() + .filter(u => u !== 0); + + const firstUser = getSelectedUserInputs() + .map((index, input) => ({ + name: input.dataset.meta, + value: parseInt(input.value, 10), + })) + .filter(u => u.id !== 0) + .get(0); + + if (selectedUsers.length === 0) { + return 'Unassigned'; + } else if (selectedUsers.length === 1) { + return firstUser.name; + } else if (isSelected) { + const otherSelected = selectedUsers.filter(s => s !== selectedUser.id); + return `${selectedUser.name} + ${otherSelected.length} more`; + } else { + return `${firstUser.name} + ${selectedUsers.length - 1} more`; + } + }; - // Save current selected user to the DOM - const input = document.createElement('input'); - input.type = 'hidden'; - input.name = $dropdown.data('field-name'); + $('.assign-to-me-link').on('click', (e) => { + e.preventDefault(); + $(e.currentTarget).hide(); - const currentUserInfo = $dropdown.data('currentUserInfo'); + if ($dropdown.data('multiSelect')) { + assignYourself(); + checkMaxSelect(); - if (currentUserInfo) { - input.value = currentUserInfo.id; - input.dataset.meta = currentUserInfo.name; - } else if (_this.currentUser) { - input.value = _this.currentUser.id; - } + const currentUserInfo = $dropdown.data('currentUserInfo'); + $dropdown.find('.dropdown-toggle-text').text(getMultiSelectDropdownTitle(currentUserInfo)).removeClass('is-default'); + } else { + const $input = $(`input[name="${$dropdown.data('field-name')}"]`); + $input.val(gon.current_user_id); + selectedId = $input.val(); + $dropdown.find('.dropdown-toggle-text').text(gon.current_user_fullname).removeClass('is-default'); + } + }); - if ($selectbox) { - $dropdown.parent().before(input); - } else { - $dropdown.after(input); - } - }; + $block.on('click', '.js-assign-yourself', (e) => { + e.preventDefault(); + return assignTo(_this.currentUser.id); + }); - if ($block[0]) { - $block[0].addEventListener('assignYourself', assignYourself); + assignTo = function(selected) { + var data; + data = {}; + data[abilityName] = {}; + data[abilityName].assignee_id = selected != null ? selected : null; + $loading.removeClass('hidden').fadeIn(); + $dropdown.trigger('loading.gl.dropdown'); + + return $.ajax({ + type: 'PUT', + dataType: 'json', + url: issueURL, + data: data + }).done(function(data) { + var user; + $dropdown.trigger('loaded.gl.dropdown'); + $loading.fadeOut(); + if (data.assignee) { + user = { + name: data.assignee.name, + username: data.assignee.username, + avatar: data.assignee.avatar_url + }; + } else { + user = { + name: 'Unassigned', + username: '', + avatar: '' + }; } - - const getSelectedUserInputs = function() { - return $selectbox - .find(`input[name="${$dropdown.data('field-name')}"]`); - }; - - const getSelected = function() { - return getSelectedUserInputs() - .map((index, input) => parseInt(input.value, 10)) - .get(); - }; - - const checkMaxSelect = function() { - const maxSelect = $dropdown.data('max-select'); - if (maxSelect) { - const selected = getSelected(); - - if (selected.length > maxSelect) { - const firstSelectedId = selected[0]; - const firstSelected = $dropdown.closest('.selectbox') - .find(`input[name='${$dropdown.data('field-name')}'][value=${firstSelectedId}]`); - - firstSelected.remove(); - emitSidebarEvent('sidebar.removeAssignee', { - id: firstSelectedId, - }); + $value.html(assigneeTemplate(user)); + $collapsedSidebar.attr('title', user.name).tooltip('fixTitle'); + return $collapsedSidebar.html(collapsedAssigneeTemplate(user)); + }); + }; + collapsedAssigneeTemplate = _.template('<% if( avatar ) { %> <% } else { %> <% } %>'); + assigneeTemplate = _.template('<% if (username) { %> <% if( avatar ) { %> <% } %> <%- name %> @<%- username %> <% } else { %> No assignee - assign yourself <% } %>'); + return $dropdown.glDropdown({ + showMenuAbove: showMenuAbove, + data: function(term, callback) { + var isAuthorFilter; + isAuthorFilter = $('.js-author-search'); + return _this.users(term, options, function(users) { + // GitLabDropdownFilter returns this.instance + // GitLabDropdownRemote returns this.options.instance + const glDropdown = this.instance || this.options.instance; + glDropdown.options.processData(term, users, callback); + }.bind(this)); + }, + processData: function(term, users, callback) { + let anyUser; + let index; + let j; + let len; + let name; + let obj; + let showDivider; + if (term.length === 0) { + showDivider = 0; + if (firstUser) { + // Move current user to the front of the list + for (index = j = 0, len = users.length; j < len; index = (j += 1)) { + obj = users[index]; + if (obj.username === firstUser) { + users.splice(index, 1); + users.unshift(obj); + break; + } } } - }; - - const getMultiSelectDropdownTitle = function(selectedUser, isSelected) { - const selectedUsers = getSelected() - .filter(u => u !== 0); - - const firstUser = getSelectedUserInputs() - .map((index, input) => ({ - name: input.dataset.meta, - value: parseInt(input.value, 10), - })) - .filter(u => u.id !== 0) - .get(0); - - if (selectedUsers.length === 0) { - return 'Unassigned'; - } else if (selectedUsers.length === 1) { - return firstUser.name; - } else if (isSelected) { - const otherSelected = selectedUsers.filter(s => s !== selectedUser.id); - return `${selectedUser.name} + ${otherSelected.length} more`; - } else { - return `${firstUser.name} + ${selectedUsers.length - 1} more`; + if (showNullUser) { + showDivider += 1; + users.unshift({ + beforeDivider: true, + name: 'Unassigned', + id: 0 + }); + } + if (showAnyUser) { + showDivider += 1; + name = showAnyUser; + if (name === true) { + name = 'Any User'; + } + anyUser = { + beforeDivider: true, + name: name, + id: null + }; + users.unshift(anyUser); } - }; - - $('.assign-to-me-link').on('click', (e) => { - e.preventDefault(); - $(e.currentTarget).hide(); - - if ($dropdown.data('multiSelect')) { - assignYourself(); - checkMaxSelect(); - const currentUserInfo = $dropdown.data('currentUserInfo'); - $dropdown.find('.dropdown-toggle-text').text(getMultiSelectDropdownTitle(currentUserInfo)).removeClass('is-default'); - } else { - const $input = $(`input[name="${$dropdown.data('field-name')}"]`); - $input.val(gon.current_user_id); - selectedId = $input.val(); - $dropdown.find('.dropdown-toggle-text').text(gon.current_user_fullname).removeClass('is-default'); + if (showDivider) { + users.splice(showDivider, 0, 'divider'); } - }); - $block.on('click', '.js-assign-yourself', (e) => { - e.preventDefault(); - return assignTo(_this.currentUser.id); - }); + if ($dropdown.hasClass('js-multiselect')) { + const selected = getSelected().filter(i => i !== 0); - assignTo = function(selected) { - var data; - data = {}; - data[abilityName] = {}; - data[abilityName].assignee_id = selected != null ? selected : null; - $loading.removeClass('hidden').fadeIn(); - $dropdown.trigger('loading.gl.dropdown'); - - return $.ajax({ - type: 'PUT', - dataType: 'json', - url: issueURL, - data: data - }).done(function(data) { - var user; - $dropdown.trigger('loaded.gl.dropdown'); - $loading.fadeOut(); - if (data.assignee) { - user = { - name: data.assignee.name, - username: data.assignee.username, - avatar: data.assignee.avatar_url - }; - } else { - user = { - name: 'Unassigned', - username: '', - avatar: '' - }; - } - $value.html(assigneeTemplate(user)); - $collapsedSidebar.attr('title', user.name).tooltip('fixTitle'); - return $collapsedSidebar.html(collapsedAssigneeTemplate(user)); - }); - }; - collapsedAssigneeTemplate = _.template('<% if( avatar ) { %> <% } else { %> <% } %>'); - assigneeTemplate = _.template('<% if (username) { %> <% if( avatar ) { %> <% } %> <%- name %> @<%- username %> <% } else { %> No assignee - assign yourself <% } %>'); - return $dropdown.glDropdown({ - showMenuAbove: showMenuAbove, - data: function(term, callback) { - var isAuthorFilter; - isAuthorFilter = $('.js-author-search'); - return _this.users(term, options, function(users) { - // GitLabDropdownFilter returns this.instance - // GitLabDropdownRemote returns this.options.instance - const glDropdown = this.instance || this.options.instance; - glDropdown.options.processData(term, users, callback); - }.bind(this)); - }, - processData: function(term, users, callback) { - let anyUser; - let index; - let j; - let len; - let name; - let obj; - let showDivider; - if (term.length === 0) { - showDivider = 0; - if (firstUser) { - // Move current user to the front of the list - for (index = j = 0, len = users.length; j < len; index = (j += 1)) { - obj = users[index]; - if (obj.username === firstUser) { - users.splice(index, 1); - users.unshift(obj); - break; - } - } - } - if (showNullUser) { + if (selected.length > 0) { + if ($dropdown.data('dropdown-header')) { showDivider += 1; - users.unshift({ - beforeDivider: true, - name: 'Unassigned', - id: 0 + users.splice(showDivider, 0, { + header: $dropdown.data('dropdown-header'), }); } - if (showAnyUser) { - showDivider += 1; - name = showAnyUser; - if (name === true) { - name = 'Any User'; - } - anyUser = { - beforeDivider: true, - name: name, - id: null - }; - users.unshift(anyUser); - } - if (showDivider) { - users.splice(showDivider, 0, 'divider'); - } + const selectedUsers = users + .filter(u => selected.indexOf(u.id) !== -1) + .sort((a, b) => a.name > b.name); - if ($dropdown.hasClass('js-multiselect')) { - const selected = getSelected().filter(i => i !== 0); + users = users.filter(u => selected.indexOf(u.id) === -1); - if (selected.length > 0) { - if ($dropdown.data('dropdown-header')) { - showDivider += 1; - users.splice(showDivider, 0, { - header: $dropdown.data('dropdown-header'), - }); - } - - const selectedUsers = users - .filter(u => selected.indexOf(u.id) !== -1) - .sort((a, b) => a.name > b.name); + selectedUsers.forEach((selectedUser) => { + showDivider += 1; + users.splice(showDivider, 0, selectedUser); + }); - users = users.filter(u => selected.indexOf(u.id) === -1); + users.splice(showDivider + 1, 0, 'divider'); + } + } + } - selectedUsers.forEach((selectedUser) => { - showDivider += 1; - users.splice(showDivider, 0, selectedUser); - }); + callback(users); + if (showMenuAbove) { + $dropdown.data('glDropdown').positionMenuAbove(); + } + }, + filterable: true, + filterRemote: true, + search: { + fields: ['name', 'username'] + }, + selectable: true, + fieldName: $dropdown.data('field-name'), + toggleLabel: function(selected, el, glDropdown) { + const inputValue = glDropdown.filterInput.val(); + + if (this.multiSelect && inputValue === '') { + // Remove non-users from the fullData array + const users = glDropdown.filteredFullData(); + const callback = glDropdown.parseData.bind(glDropdown); + + // Update the data model + this.processData(inputValue, users, callback); + } - users.splice(showDivider + 1, 0, 'divider'); - } - } - } + if (this.multiSelect) { + return getMultiSelectDropdownTitle(selected, $(el).hasClass('is-active')); + } - callback(users); - if (showMenuAbove) { - $dropdown.data('glDropdown').positionMenuAbove(); - } - }, - filterable: true, - filterRemote: true, - search: { - fields: ['name', 'username'] - }, - selectable: true, - fieldName: $dropdown.data('field-name'), - toggleLabel: function(selected, el, glDropdown) { - const inputValue = glDropdown.filterInput.val(); - - if (this.multiSelect && inputValue === '') { - // Remove non-users from the fullData array - const users = glDropdown.filteredFullData(); - const callback = glDropdown.parseData.bind(glDropdown); - - // Update the data model - this.processData(inputValue, users, callback); - } + if (selected && 'id' in selected && $(el).hasClass('is-active')) { + $dropdown.find('.dropdown-toggle-text').removeClass('is-default'); + if (selected.text) { + return selected.text; + } else { + return selected.name; + } + } else { + $dropdown.find('.dropdown-toggle-text').addClass('is-default'); + return defaultLabel; + } + }, + defaultLabel: defaultLabel, + hidden: function(e) { + if ($dropdown.hasClass('js-multiselect')) { + emitSidebarEvent('sidebar.saveAssignees'); + } - if (this.multiSelect) { - return getMultiSelectDropdownTitle(selected, $(el).hasClass('is-active')); - } + if (!$dropdown.data('always-show-selectbox')) { + $selectbox.hide(); - if (selected && 'id' in selected && $(el).hasClass('is-active')) { - $dropdown.find('.dropdown-toggle-text').removeClass('is-default'); - if (selected.text) { - return selected.text; - } else { - return selected.name; - } - } else { - $dropdown.find('.dropdown-toggle-text').addClass('is-default'); - return defaultLabel; - } - }, - defaultLabel: defaultLabel, - hidden: function(e) { - if ($dropdown.hasClass('js-multiselect')) { - emitSidebarEvent('sidebar.saveAssignees'); - } + // Recalculate where .value is because vue might have changed it + $block = $selectbox.closest('.block'); + $value = $block.find('.value'); + // display:block overrides the hide-collapse rule + $value.css('display', ''); + } + }, + multiSelect: $dropdown.hasClass('js-multiselect'), + inputMeta: $dropdown.data('input-meta'), + clicked: function(options) { + const { $el, e, isMarking } = options; + const user = options.selectedObj; + + if ($dropdown.hasClass('js-multiselect')) { + const isActive = $el.hasClass('is-active'); + const previouslySelected = $dropdown.closest('.selectbox') + .find("input[name='" + ($dropdown.data('field-name')) + "'][value!=0]"); + + // Enables support for limiting the number of users selected + // Automatically removes the first on the list if more users are selected + checkMaxSelect(); + + if (user.beforeDivider && user.name.toLowerCase() === 'unassigned') { + // Unassigned selected + previouslySelected.each((index, element) => { + const id = parseInt(element.value, 10); + element.remove(); + }); + emitSidebarEvent('sidebar.removeAllAssignees'); + } else if (isActive) { + // user selected + emitSidebarEvent('sidebar.addAssignee', user); - if (!$dropdown.data('always-show-selectbox')) { - $selectbox.hide(); + // Remove unassigned selection (if it was previously selected) + const unassignedSelected = $dropdown.closest('.selectbox') + .find("input[name='" + ($dropdown.data('field-name')) + "'][value=0]"); - // Recalculate where .value is because vue might have changed it - $block = $selectbox.closest('.block'); - $value = $block.find('.value'); - // display:block overrides the hide-collapse rule - $value.css('display', ''); + if (unassignedSelected) { + unassignedSelected.remove(); + } + } else { + if (previouslySelected.length === 0) { + // Select unassigned because there is no more selected users + this.addInput($dropdown.data('field-name'), 0, {}); } - }, - multiSelect: $dropdown.hasClass('js-multiselect'), - inputMeta: $dropdown.data('input-meta'), - clicked: function(options) { - const { $el, e, isMarking } = options; - const user = options.selectedObj; - - if ($dropdown.hasClass('js-multiselect')) { - const isActive = $el.hasClass('is-active'); - const previouslySelected = $dropdown.closest('.selectbox') - .find("input[name='" + ($dropdown.data('field-name')) + "'][value!=0]"); - - // Enables support for limiting the number of users selected - // Automatically removes the first on the list if more users are selected - checkMaxSelect(); - - if (user.beforeDivider && user.name.toLowerCase() === 'unassigned') { - // Unassigned selected - previouslySelected.each((index, element) => { - const id = parseInt(element.value, 10); - element.remove(); - }); - emitSidebarEvent('sidebar.removeAllAssignees'); - } else if (isActive) { - // user selected - emitSidebarEvent('sidebar.addAssignee', user); - // Remove unassigned selection (if it was previously selected) - const unassignedSelected = $dropdown.closest('.selectbox') - .find("input[name='" + ($dropdown.data('field-name')) + "'][value=0]"); + // User unselected + emitSidebarEvent('sidebar.removeAssignee', user); + } - if (unassignedSelected) { - unassignedSelected.remove(); - } - } else { - if (previouslySelected.length === 0) { - // Select unassigned because there is no more selected users - this.addInput($dropdown.data('field-name'), 0, {}); - } + if (getSelected().find(u => u === gon.current_user_id)) { + $('.assign-to-me-link').hide(); + } else { + $('.assign-to-me-link').show(); + } + } - // User unselected - emitSidebarEvent('sidebar.removeAssignee', user); - } + var isIssueIndex, isMRIndex, page, selected; + page = $('body').data('page'); + isIssueIndex = page === 'projects:issues:index'; + isMRIndex = (page === page && page === 'projects:merge_requests:index'); + if ($dropdown.hasClass('js-filter-bulk-update') || $dropdown.hasClass('js-issuable-form-dropdown')) { + e.preventDefault(); - if (getSelected().find(u => u === gon.current_user_id)) { - $('.assign-to-me-link').hide(); - } else { - $('.assign-to-me-link').show(); - } - } + const isSelecting = (user.id !== selectedId); + selectedId = isSelecting ? user.id : selectedIdDefault; - var isIssueIndex, isMRIndex, page, selected; - page = $('body').data('page'); - isIssueIndex = page === 'projects:issues:index'; - isMRIndex = (page === page && page === 'projects:merge_requests:index'); - if ($dropdown.hasClass('js-filter-bulk-update') || $dropdown.hasClass('js-issuable-form-dropdown')) { - e.preventDefault(); + if (selectedId === gon.current_user_id) { + $('.assign-to-me-link').hide(); + } else { + $('.assign-to-me-link').show(); + } + return; + } + if ($el.closest('.add-issues-modal').length) { + gl.issueBoards.ModalStore.store.filter[$dropdown.data('field-name')] = user.id; + } else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { + return Issuable.filterResults($dropdown.closest('form')); + } else if ($dropdown.hasClass('js-filter-submit')) { + return $dropdown.closest('form').submit(); + } else if (!$dropdown.hasClass('js-multiselect')) { + selected = $dropdown.closest('.selectbox').find("input[name='" + ($dropdown.data('field-name')) + "']").val(); + return assignTo(selected); + } - const isSelecting = (user.id !== selectedId); - selectedId = isSelecting ? user.id : selectedIdDefault; + // Automatically close dropdown after assignee is selected + // since CE has no multiple assignees + // EE does not have a max-select + if ($dropdown.data('max-select') && + getSelected().length === $dropdown.data('max-select')) { + // Close the dropdown + this.el.click(); - if (selectedId === gon.current_user_id) { - $('.assign-to-me-link').hide(); - } else { - $('.assign-to-me-link').show(); - } - return; - } - if ($el.closest('.add-issues-modal').length) { - gl.issueBoards.ModalStore.store.filter[$dropdown.data('field-name')] = user.id; - } else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { - return Issuable.filterResults($dropdown.closest('form')); - } else if ($dropdown.hasClass('js-filter-submit')) { - return $dropdown.closest('form').submit(); - } else if (!$dropdown.hasClass('js-multiselect')) { - selected = $dropdown.closest('.selectbox').find("input[name='" + ($dropdown.data('field-name')) + "']").val(); - return assignTo(selected); - } + this.hidden(); + } + }, + id: function (user) { + return user.id; + }, + opened: function(e) { + const $el = $(e.currentTarget); + if ($dropdown.hasClass('js-issue-board-sidebar')) { + selectedId = parseInt($dropdown[0].dataset.selected, 10) || selectedIdDefault; + } + $el.find('.is-active').removeClass('is-active'); - // Automatically close dropdown after assignee is selected - // since CE has no multiple assignees - // EE does not have a max-select - if ($dropdown.data('max-select') && - getSelected().length === $dropdown.data('max-select')) { - // Close the dropdown - this.el.click(); + function highlightSelected(id) { + $el.find(`li[data-user-id="${id}"] .dropdown-menu-user-link`).addClass('is-active'); + } - this.hidden(); - } - }, - id: function (user) { - return user.id; - }, - opened: function(e) { - const $el = $(e.currentTarget); - if ($dropdown.hasClass('js-issue-board-sidebar')) { - selectedId = parseInt($dropdown[0].dataset.selected, 10) || selectedIdDefault; - } - $el.find('.is-active').removeClass('is-active'); + if ($selectbox[0]) { + getSelected().forEach(selectedId => highlightSelected(selectedId)); + } else { + highlightSelected(selectedId); + } + }, + updateLabel: $dropdown.data('dropdown-title'), + renderRow: function(user) { + var avatar, img, listClosingTags, listWithName, listWithUserName, username; + username = user.username ? "@" + user.username : ""; + avatar = user.avatar_url ? user.avatar_url : false; - function highlightSelected(id) { - $el.find(`li[data-user-id="${id}"] .dropdown-menu-user-link`).addClass('is-active'); - } + let selected = user.id === parseInt(selectedId, 10); - if ($selectbox[0]) { - getSelected().forEach(selectedId => highlightSelected(selectedId)); - } else { - highlightSelected(selectedId); - } - }, - updateLabel: $dropdown.data('dropdown-title'), - renderRow: function(user) { - var avatar, img, listClosingTags, listWithName, listWithUserName, username; - username = user.username ? "@" + user.username : ""; - avatar = user.avatar_url ? user.avatar_url : false; + if (this.multiSelect) { + const fieldName = this.fieldName; + const field = $dropdown.closest('.selectbox').find("input[name='" + fieldName + "'][value='" + user.id + "']"); - let selected = user.id === parseInt(selectedId, 10); + if (field.length) { + selected = true; + } + } - if (this.multiSelect) { - const fieldName = this.fieldName; - const field = $dropdown.closest('.selectbox').find("input[name='" + fieldName + "'][value='" + user.id + "']"); + img = ""; + if (user.beforeDivider != null) { + `
  • ${user.name}
  • `; + } else { + if (avatar) { + img = ""; + } + } - if (field.length) { - selected = true; + return ` +
  • + + ${img} + + ${user.name} + + ${username ? `${username}` : ''} + +
  • + `; + } + }); + }; + })(this)); + $('.ajax-users-select').each((function(_this) { + return function(i, select) { + var firstUser, showAnyUser, showEmailUser, showNullUser; + var options = {}; + options.skipLdap = $(select).hasClass('skip_ldap'); + options.projectId = $(select).data('project-id'); + options.groupId = $(select).data('group-id'); + options.showCurrentUser = $(select).data('current-user'); + options.pushCodeToProtectedBranches = $(select).data('push-code-to-protected-branches'); + options.authorId = $(select).data('author-id'); + options.skipUsers = $(select).data('skip-users'); + showNullUser = $(select).data('null-user'); + showAnyUser = $(select).data('any-user'); + showEmailUser = $(select).data('email-user'); + firstUser = $(select).data('first-user'); + return $(select).select2({ + placeholder: "Search for a user", + multiple: $(select).hasClass('multiselect'), + minimumInputLength: 0, + query: function(query) { + return _this.users(query.term, options, function(users) { + var anyUser, data, emailUser, index, j, len, name, nullUser, obj, ref; + data = { + results: users + }; + if (query.term.length === 0) { + if (firstUser) { + // Move current user to the front of the list + ref = data.results; + for (index = j = 0, len = ref.length; j < len; index = (j += 1)) { + obj = ref[index]; + if (obj.username === firstUser) { + data.results.splice(index, 1); + data.results.unshift(obj); + break; + } } } - - img = ""; - if (user.beforeDivider != null) { - `
  • ${user.name}
  • `; - } else { - if (avatar) { - img = ""; + if (showNullUser) { + nullUser = { + name: 'Unassigned', + id: 0 + }; + data.results.unshift(nullUser); + } + if (showAnyUser) { + name = showAnyUser; + if (name === true) { + name = 'Any User'; } + anyUser = { + name: name, + id: null + }; + data.results.unshift(anyUser); } - - return ` -
  • - - ${img} - - ${user.name} - - ${username ? `${username}` : ''} - -
  • - `; } - }); - }; - })(this)); - $('.ajax-users-select').each((function(_this) { - return function(i, select) { - var firstUser, showAnyUser, showEmailUser, showNullUser; - var options = {}; - options.skipLdap = $(select).hasClass('skip_ldap'); - options.projectId = $(select).data('project-id'); - options.groupId = $(select).data('group-id'); - options.showCurrentUser = $(select).data('current-user'); - options.pushCodeToProtectedBranches = $(select).data('push-code-to-protected-branches'); - options.authorId = $(select).data('author-id'); - options.skipUsers = $(select).data('skip-users'); - showNullUser = $(select).data('null-user'); - showAnyUser = $(select).data('any-user'); - showEmailUser = $(select).data('email-user'); - firstUser = $(select).data('first-user'); - return $(select).select2({ - placeholder: "Search for a user", - multiple: $(select).hasClass('multiselect'), - minimumInputLength: 0, - query: function(query) { - return _this.users(query.term, options, function(users) { - var anyUser, data, emailUser, index, j, len, name, nullUser, obj, ref; - data = { - results: users - }; - if (query.term.length === 0) { - if (firstUser) { - // Move current user to the front of the list - ref = data.results; - for (index = j = 0, len = ref.length; j < len; index = (j += 1)) { - obj = ref[index]; - if (obj.username === firstUser) { - data.results.splice(index, 1); - data.results.unshift(obj); - break; - } - } - } - if (showNullUser) { - nullUser = { - name: 'Unassigned', - id: 0 - }; - data.results.unshift(nullUser); - } - if (showAnyUser) { - name = showAnyUser; - if (name === true) { - name = 'Any User'; - } - anyUser = { - name: name, - id: null - }; - data.results.unshift(anyUser); - } - } - if (showEmailUser && data.results.length === 0 && query.term.match(/^[^@]+@[^@]+$/)) { - var trimmed = query.term.trim(); - emailUser = { - name: "Invite \"" + query.term + "\"", - username: trimmed, - id: trimmed - }; - data.results.unshift(emailUser); - } - return query.callback(data); - }); - }, - initSelection: function() { - var args; - args = 1 <= arguments.length ? slice.call(arguments, 0) : []; - return _this.initSelection.apply(_this, args); - }, - formatResult: function() { - var args; - args = 1 <= arguments.length ? slice.call(arguments, 0) : []; - return _this.formatResult.apply(_this, args); - }, - formatSelection: function() { - var args; - args = 1 <= arguments.length ? slice.call(arguments, 0) : []; - return _this.formatSelection.apply(_this, args); - }, - dropdownCssClass: "ajax-users-dropdown", - // we do not want to escape markup since we are displaying html in results - escapeMarkup: function(m) { - return m; + if (showEmailUser && data.results.length === 0 && query.term.match(/^[^@]+@[^@]+$/)) { + var trimmed = query.term.trim(); + emailUser = { + name: "Invite \"" + query.term + "\"", + username: trimmed, + id: trimmed + }; + data.results.unshift(emailUser); } + return query.callback(data); }); - }; - })(this)); - } - - UsersSelect.prototype.initSelection = function(element, callback) { - var id, nullUser; - id = $(element).val(); - if (id === "0") { - nullUser = { - name: 'Unassigned' - }; - return callback(nullUser); - } else if (id !== "") { - return this.user(id, callback); - } - }; - - UsersSelect.prototype.formatResult = function(user) { - var avatar; - if (user.avatar_url) { - avatar = user.avatar_url; - } else { - avatar = gon.default_avatar_url; - } - return "
    " + user.name + "
    " + (user.username || "") + "
    "; - }; - - UsersSelect.prototype.formatSelection = function(user) { - return user.name; - }; - - UsersSelect.prototype.user = function(user_id, callback) { - if (!/^\d+$/.test(user_id)) { - return false; - } - - var url; - url = this.buildUrl(this.userPath); - url = url.replace(':id', user_id); - return $.ajax({ - url: url, - dataType: "json" - }).done(function(user) { - return callback(user); - }); - }; - - // Return users list. Filtered by query - // Only active users retrieved - UsersSelect.prototype.users = function(query, options, callback) { - var url; - url = this.buildUrl(this.usersPath); - return $.ajax({ - url: url, - data: { - search: query, - per_page: 20, - active: true, - project_id: options.projectId || null, - group_id: options.groupId || null, - skip_ldap: options.skipLdap || null, - todo_filter: options.todoFilter || null, - todo_state_filter: options.todoStateFilter || null, - current_user: options.showCurrentUser || null, - push_code_to_protected_branches: options.pushCodeToProtectedBranches || null, - author_id: options.authorId || null, - skip_users: options.skipUsers || null }, - dataType: "json" - }).done(function(users) { - return callback(users); + initSelection: function() { + var args; + args = 1 <= arguments.length ? [].slice.call(arguments, 0) : []; + return _this.initSelection.apply(_this, args); + }, + formatResult: function() { + var args; + args = 1 <= arguments.length ? [].slice.call(arguments, 0) : []; + return _this.formatResult.apply(_this, args); + }, + formatSelection: function() { + var args; + args = 1 <= arguments.length ? [].slice.call(arguments, 0) : []; + return _this.formatSelection.apply(_this, args); + }, + dropdownCssClass: "ajax-users-dropdown", + // we do not want to escape markup since we are displaying html in results + escapeMarkup: function(m) { + return m; + } }); }; - - UsersSelect.prototype.buildUrl = function(url) { - if (gon.relative_url_root != null) { - url = gon.relative_url_root.replace(/\/$/, '') + url; - } - return url; + })(this)); +} + +UsersSelect.prototype.initSelection = function(element, callback) { + var id, nullUser; + id = $(element).val(); + if (id === "0") { + nullUser = { + name: 'Unassigned' }; - - return UsersSelect; - })(); -}).call(window); + return callback(nullUser); + } else if (id !== "") { + return this.user(id, callback); + } +}; + +UsersSelect.prototype.formatResult = function(user) { + var avatar; + if (user.avatar_url) { + avatar = user.avatar_url; + } else { + avatar = gon.default_avatar_url; + } + return "
    " + user.name + "
    " + (user.username || "") + "
    "; +}; + +UsersSelect.prototype.formatSelection = function(user) { + return user.name; +}; + +UsersSelect.prototype.user = function(user_id, callback) { + if (!/^\d+$/.test(user_id)) { + return false; + } + + var url; + url = this.buildUrl(this.userPath); + url = url.replace(':id', user_id); + return $.ajax({ + url: url, + dataType: "json" + }).done(function(user) { + return callback(user); + }); +}; + +// Return users list. Filtered by query +// Only active users retrieved +UsersSelect.prototype.users = function(query, options, callback) { + var url; + url = this.buildUrl(this.usersPath); + return $.ajax({ + url: url, + data: { + search: query, + per_page: 20, + active: true, + project_id: options.projectId || null, + group_id: options.groupId || null, + skip_ldap: options.skipLdap || null, + todo_filter: options.todoFilter || null, + todo_state_filter: options.todoStateFilter || null, + current_user: options.showCurrentUser || null, + push_code_to_protected_branches: options.pushCodeToProtectedBranches || null, + author_id: options.authorId || null, + skip_users: options.skipUsers || null + }, + dataType: "json" + }).done(function(users) { + return callback(users); + }); +}; + +UsersSelect.prototype.buildUrl = function(url) { + if (gon.relative_url_root != null) { + url = gon.relative_url_root.replace(/\/$/, '') + url; + } + return url; +}; + +export default UsersSelect; -- cgit v1.2.1 From 16da7f2365fe67c078a2f10766662b93fe8576fc Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Fri, 12 May 2017 05:19:32 -0500 Subject: Fix linking to resolved note in diff Fix https://gitlab.com/gitlab-org/gitlab-ce/issues/32125 --- .../diff_notes/components/diff_note_avatars.js | 2 +- app/assets/javascripts/merge_request_tabs.js | 18 +++++- app/assets/javascripts/notes.js | 70 +++++++++++++--------- app/assets/stylesheets/framework/timeline.scss | 63 +++++++++---------- 4 files changed, 91 insertions(+), 62 deletions(-) diff --git a/app/assets/javascripts/diff_notes/components/diff_note_avatars.js b/app/assets/javascripts/diff_notes/components/diff_note_avatars.js index f3a688fbf2f..5f533b5761c 100644 --- a/app/assets/javascripts/diff_notes/components/diff_note_avatars.js +++ b/app/assets/javascripts/diff_notes/components/diff_note_avatars.js @@ -120,7 +120,7 @@ const DiffNoteAvatars = Vue.extend({ }, methods: { clickedAvatar(e) { - notes.addDiffNote(e); + notes.onAddDiffNote(e); // Toggle the active state of the toggle all button this.toggleDiscussionsToggleState(); diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 93c30c54a8e..efbfdbcee0c 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -1,6 +1,7 @@ /* eslint-disable no-new, class-methods-use-this */ /* global Breakpoints */ /* global Flash */ +/* global notes */ import Cookies from 'js-cookie'; import './breakpoints'; @@ -251,7 +252,8 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion'; this.ajaxGet({ url: `${urlPathname}.json${location.search}`, success: (data) => { - $('#diffs').html(data.html); + const $container = $('#diffs'); + $container.html(data.html); if (typeof gl.diffNotesCompileComponents !== 'undefined') { gl.diffNotesCompileComponents(); @@ -278,6 +280,20 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion'; }) .init(); }); + + // Scroll any linked note into view + // Similar to `toggler_behavior` in the discussion tab + const hash = window.gl.utils.getLocationHash(); + const anchor = hash && $container.find(`[id="${hash}"]`); + if (anchor) { + const notesContent = anchor.closest('.notes_content'); + const lineType = notesContent.hasClass('new') ? 'new' : 'old'; + notes.addDiffNote(anchor, lineType, false); + anchor[0].scrollIntoView(); + // We have multiple elements on the page with `#note_xxx` + // (discussion and diff tabs) and `:target` only applies to the first + anchor.addClass('target'); + } }, }); } diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index f6fe6d9f0fd..73396ca47fe 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -33,9 +33,9 @@ const normalizeNewlines = function(str) { this.updateComment = this.updateComment.bind(this); this.visibilityChange = this.visibilityChange.bind(this); this.cancelDiscussionForm = this.cancelDiscussionForm.bind(this); - this.addDiffNote = this.addDiffNote.bind(this); + this.onAddDiffNote = this.onAddDiffNote.bind(this); this.setupDiscussionNoteForm = this.setupDiscussionNoteForm.bind(this); - this.replyToDiscussionNote = this.replyToDiscussionNote.bind(this); + this.onReplyToDiscussionNote = this.onReplyToDiscussionNote.bind(this); this.removeNote = this.removeNote.bind(this); this.cancelEdit = this.cancelEdit.bind(this); this.updateNote = this.updateNote.bind(this); @@ -100,9 +100,9 @@ const normalizeNewlines = function(str) { // update the file name when an attachment is selected $(document).on("change", ".js-note-attachment-input", this.updateFormAttachment); // reply to diff/discussion notes - $(document).on("click", ".js-discussion-reply-button", this.replyToDiscussionNote); + $(document).on("click", ".js-discussion-reply-button", this.onReplyToDiscussionNote); // add diff note - $(document).on("click", ".js-add-diff-note-button", this.addDiffNote); + $(document).on("click", ".js-add-diff-note-button", this.onAddDiffNote); // hide diff note form $(document).on("click", ".js-close-discussion-note-form", this.cancelDiscussionForm); // toggle commit list @@ -794,10 +794,14 @@ const normalizeNewlines = function(str) { Shows the note form below the notes. */ - Notes.prototype.replyToDiscussionNote = function(e) { + Notes.prototype.onReplyToDiscussionNote = function(e) { + this.replyToDiscussionNote(e.target); + }; + + Notes.prototype.replyToDiscussionNote = function(target) { var form, replyLink; form = this.cleanForm(this.formClone.clone()); - replyLink = $(e.target).closest(".js-discussion-reply-button"); + replyLink = $(target).closest(".js-discussion-reply-button"); // insert the form after the button replyLink .closest('.discussion-reply-holder') @@ -867,35 +871,43 @@ const normalizeNewlines = function(str) { Sets up the form and shows it. */ - Notes.prototype.addDiffNote = function(e) { - var $link, addForm, hasNotes, lineType, newForm, nextRow, noteForm, notesContent, notesContentSelector, replyButton, row, rowCssToAdd, targetContent, isDiffCommentAvatar; + Notes.prototype.onAddDiffNote = function(e) { e.preventDefault(); - $link = $(e.currentTarget || e.target); + const $link = $(e.currentTarget || e.target); + const showReplyInput = !$link.hasClass('js-diff-comment-avatar'); + this.addDiffNote($link, $link.data('lineType'), showReplyInput); + }; + + Notes.prototype.addDiffNote = function(target, lineType, showReplyInput) { + var $link, addForm, hasNotes, newForm, noteForm, replyButton, row, rowCssToAdd, targetContent, isDiffCommentAvatar; + $link = $(target); row = $link.closest("tr"); - nextRow = row.next(); - hasNotes = nextRow.is(".notes_holder"); + const nextRow = row.next(); + let targetRow = row; + if (nextRow.is('.notes_holder')) { + targetRow = nextRow; + } + + hasNotes = targetRow.is(".notes_holder"); addForm = false; - notesContentSelector = ".notes_content"; + let lineTypeSelector = ''; rowCssToAdd = "
    "; - isDiffCommentAvatar = $link.hasClass('js-diff-comment-avatar'); // In parallel view, look inside the correct left/right pane if (this.isParallelView()) { - lineType = $link.data("lineType"); - notesContentSelector += "." + lineType; + lineTypeSelector = `.${lineType}`; rowCssToAdd = "
    "; } - notesContentSelector += " .content"; - notesContent = nextRow.find(notesContentSelector); + const notesContentSelector = `.notes_content${lineTypeSelector} .content`; + let notesContent = targetRow.find(notesContentSelector); - if (hasNotes && !isDiffCommentAvatar) { - nextRow.show(); - notesContent = nextRow.find(notesContentSelector); + if (hasNotes && showReplyInput) { + targetRow.show(); + notesContent = targetRow.find(notesContentSelector); if (notesContent.length) { notesContent.show(); replyButton = notesContent.find(".js-discussion-reply-button:visible"); if (replyButton.length) { - e.target = replyButton[0]; - $.proxy(this.replyToDiscussionNote, replyButton[0], e).call(); + this.replyToDiscussionNote(replyButton[0]); } else { // In parallel view, the form may not be present in one of the panes noteForm = notesContent.find(".js-discussion-note-form"); @@ -904,18 +916,18 @@ const normalizeNewlines = function(str) { } } } - } else if (!isDiffCommentAvatar) { + } else if (showReplyInput) { // add a notes row and insert the form row.after(rowCssToAdd); - nextRow = row.next(); - notesContent = nextRow.find(notesContentSelector); + targetRow = row.next(); + notesContent = targetRow.find(notesContentSelector); addForm = true; } else { - nextRow.show(); + targetRow.show(); notesContent.toggle(!notesContent.is(':visible')); - if (!nextRow.find('.content:not(:empty)').is(':visible')) { - nextRow.hide(); + if (!targetRow.find('.content:not(:empty)').is(':visible')) { + targetRow.hide(); } } @@ -1320,7 +1332,7 @@ const normalizeNewlines = function(str) { // Show form again on UI on failure if (isDiscussionForm && $notesContainer.length) { const replyButton = $notesContainer.parent().find('.js-discussion-reply-button'); - $.proxy(this.replyToDiscussionNote, replyButton[0], { target: replyButton[0] }).call(); + this.replyToDiscussionNote(replyButton[0]); $form = $notesContainer.parent().find('form'); } diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss index d2164a1d333..aa0c512a277 100644 --- a/app/assets/stylesheets/framework/timeline.scss +++ b/app/assets/stylesheets/framework/timeline.scss @@ -3,30 +3,6 @@ margin: 0; padding: 0; - .timeline-entry { - padding: $gl-padding $gl-btn-padding 0; - border-color: $white-normal; - color: $gl-text-color; - border-bottom: 1px solid $border-white-light; - - .timeline-entry-inner { - position: relative; - } - - &:target { - background: $line-target-blue; - } - - .avatar { - margin-right: 15px; - } - - .controls { - padding-top: 10px; - float: right; - } - } - .note-text { p:last-child { margin-bottom: 0; @@ -46,20 +22,45 @@ } } +.timeline-entry { + padding: $gl-padding $gl-btn-padding 0; + border-color: $white-normal; + color: $gl-text-color; + border-bottom: 1px solid $border-white-light; + + .timeline-entry-inner { + position: relative; + } + + &:target, + &.target { + background: $line-target-blue; + } + + .avatar { + margin-right: 15px; + } + + .controls { + padding-top: 10px; + float: right; + } +} + @media (max-width: $screen-xs-max) { .timeline { &::before { background: none; } + } - .timeline-entry .timeline-entry-inner { - .timeline-icon { - display: none; - } + .timeline-entry .timeline-entry-inner { + .timeline-icon { + display: none; + } - .timeline-content { - margin-left: 0; - } + .timeline-content { + margin-left: 0; } } } -- cgit v1.2.1 From 38d84f83600d890f4227fc510a484554f009a1c0 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Fri, 12 May 2017 14:31:05 -0500 Subject: Select dependency linker based on file type --- lib/gitlab/dependency_linker/base_linker.rb | 10 ++++++++-- lib/gitlab/dependency_linker/gemfile_linker.rb | 4 +--- lib/gitlab/file_detector.rb | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/gitlab/dependency_linker/base_linker.rb b/lib/gitlab/dependency_linker/base_linker.rb index 40a4ad11372..5f4027e7e81 100644 --- a/lib/gitlab/dependency_linker/base_linker.rb +++ b/lib/gitlab/dependency_linker/base_linker.rb @@ -1,8 +1,14 @@ module Gitlab module DependencyLinker class BaseLinker - def self.link(plain_text, highlighted_text) - new(plain_text, highlighted_text).link + class_attribute :file_type + + def self.support?(blob_name) + Gitlab::FileDetector.type_of(blob_name) == file_type + end + + def self.link(*args) + new(*args).link end attr_accessor :plain_text, :highlighted_text diff --git a/lib/gitlab/dependency_linker/gemfile_linker.rb b/lib/gitlab/dependency_linker/gemfile_linker.rb index 45be760d89e..9b82e126528 100644 --- a/lib/gitlab/dependency_linker/gemfile_linker.rb +++ b/lib/gitlab/dependency_linker/gemfile_linker.rb @@ -1,9 +1,7 @@ module Gitlab module DependencyLinker class GemfileLinker < BaseLinker - def self.support?(blob_name) - blob_name == 'Gemfile' || blob_name == 'gems.rb' - end + self.file_type = :gemfile private diff --git a/lib/gitlab/file_detector.rb b/lib/gitlab/file_detector.rb index f8b3d0b4965..c6a89597b23 100644 --- a/lib/gitlab/file_detector.rb +++ b/lib/gitlab/file_detector.rb @@ -12,6 +12,7 @@ module Gitlab version: 'version', gitignore: '.gitignore', koding: '.koding.yml', + gemfile: /\A(Gemfile|gems\.rb)\z/, gitlab_ci: '.gitlab-ci.yml', avatar: /\Alogo\.(png|jpg|gif)\z/, route_map: 'route-map.yml' -- cgit v1.2.1 From 179e3e5b65a460d78dc30258dd19e99d0780fe7f Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Sat, 13 May 2017 02:19:44 +0100 Subject: Added ci-error class and applied a right margin to the ci error widget state --- .../vue_merge_request_widget/components/mr_widget_pipeline.js | 2 +- app/assets/stylesheets/pages/merge_requests.scss | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.js index 517838f92ac..c02e10128e2 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.js @@ -31,7 +31,7 @@ export default {