Skip to content
Snippets Groups Projects
Commit b484ebf5 authored by Phil Hughes's avatar Phil Hughes Committed by Timothy Andrew
Browse files

Merge branch '2472-assignee-dropdown-does-not-display-selected-assignee-ce' into 'master'

Assignee dropdown does not display selected assignee

Closes #2472

See merge request !11744
 Conflicts:
	app/assets/javascripts/users_select.js
parent d27721e9
No related branches found
No related tags found
1 merge request!12002Fix 9.2 conflicts for "Assignee dropdown does not display selected assignee"
Pipeline #
Loading
Loading
@@ -469,8 +469,8 @@ GitLabDropdown = (function() {
 
// Process the data to make sure rendered data
// matches the correct layout
if (this.fullData && hasMultiSelect && this.options.processData) {
const inputValue = this.filterInput.val();
const inputValue = this.filterInput.val();
if (this.fullData && hasMultiSelect && this.options.processData && inputValue.length === 0) {
this.options.processData.call(this.options, inputValue, this.filteredFullData(), this.parseData.bind(this));
}
 
Loading
Loading
@@ -741,6 +741,12 @@ GitLabDropdown = (function() {
$input.attr('id', this.options.inputId);
}
 
if (this.options.multiSelect) {
Object.keys(selectedObject).forEach((attribute) => {
$input.attr(`data-${attribute}`, selectedObject[attribute]);
});
}
if (this.options.inputMeta) {
$input.attr('data-meta', selectedObject[this.options.inputMeta]);
}
Loading
Loading
Loading
Loading
@@ -5,6 +5,7 @@
// TODO: remove eventHub hack after code splitting refactor
window.emitSidebarEvent = window.emitSidebarEvent || $.noop;
 
<<<<<<< HEAD
(function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; },
slice = [].slice;
Loading
Loading
@@ -19,6 +20,84 @@ window.emitSidebarEvent = window.emitSidebarEvent || $.noop;
if (currentUser != null) {
if (typeof currentUser === 'object') {
this.currentUser = 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');
options.perPage = $dropdown.data('per-page');
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');
if (selectedId === undefined) {
selectedId = selectedIdDefault;
}
const assignYourself = function () {
const unassignedSelected = $dropdown.closest('.selectbox')
.find(`input[name='${$dropdown.data('field-name')}'][value=0]`);
if (unassignedSelected) {
unassignedSelected.remove();
}
// 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 ($selectbox) {
$dropdown.parent().before(input);
>>>>>>> 38d9538... Merge branch '2472-assignee-dropdown-does-not-display-selected-assignee-ce' into 'master'
} else {
this.currentUser = JSON.parse(currentUser);
}
Loading
Loading
@@ -76,11 +155,112 @@ window.emitSidebarEvent = window.emitSidebarEvent || $.noop;
 
const currentUserInfo = $dropdown.data('currentUserInfo');
 
<<<<<<< HEAD
if (currentUserInfo) {
input.value = currentUserInfo.id;
input.dataset.meta = currentUserInfo.name;
} else if (_this.currentUser) {
input.value = _this.currentUser.id;
=======
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 ) { %> <a class="author_link" href="/<%- username %>"> <img width="24" class="avatar avatar-inline s24" alt="" src="<%- avatar %>"> </a> <% } else { %> <i class="fa fa-user"></i> <% } %>');
assigneeTemplate = _.template('<% if (username) { %> <a class="author_link bold" href="/<%- username %>"> <% if( avatar ) { %> <img width="32" class="avatar avatar-inline s32" alt="" src="<%- avatar %>"> <% } %> <span class="author"><%- name %></span> <span class="username"> @<%- username %> </span> </a> <% } else { %> <span class="no-value assign-yourself"> No assignee - <a href="#" class="js-assign-yourself"> assign yourself </a> </span> <% } %>');
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, data, callback) {
let users = data;
// Only show assigned user list when there is no search term
if ($dropdown.hasClass('js-multiselect') && term.length === 0) {
const selectedInputs = getSelectedUserInputs();
// Potential duplicate entries when dealing with issue board
// because issue board is also managed by vue
const selectedUsers = _.uniq(selectedInputs, false, a => a.value)
.filter((input) => {
const userId = parseInt(input.value, 10);
const inUsersArray = users.find(u => u.id === userId);
return !inUsersArray && userId !== 0;
})
.map((input) => {
const userId = parseInt(input.value, 10);
const { avatarUrl, avatar_url, name, username } = input.dataset;
return {
avatar_url: avatarUrl || avatar_url,
id: userId,
name,
username,
};
});
users = data.concat(selectedUsers);
}
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;
}
}
>>>>>>> 38d9538... Merge branch '2472-assignee-dropdown-does-not-display-selected-assignee-ce' into 'master'
}
 
if ($selectbox) {
Loading
Loading
@@ -674,7 +854,81 @@ window.emitSidebarEvent = window.emitSidebarEvent || $.noop;
}
return url;
};
<<<<<<< HEAD
 
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 "<div class='user-result " + (!user.username ? 'no-username' : void 0) + "'> <div class='user-image'><img class='avatar s24' src='" + avatar + "'></div> <div class='user-name'>" + user.name + "</div> <div class='user-username'>" + (user.username || "") + "</div> </div>";
};
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: options.perPage || 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;
>>>>>>> 38d9538... Merge branch '2472-assignee-dropdown-does-not-display-selected-assignee-ce' into 'master'
Loading
Loading
@@ -14,7 +14,10 @@
name: "issue[assignee_ids][]",
":value" => "assignee.id",
"v-if" => "issue.assignees",
"v-for" => "assignee in issue.assignees" }
"v-for" => "assignee in issue.assignees",
":data-avatar_url" => "assignee.avatar",
":data-name" => "assignee.name",
":data-username" => "assignee.username" }
.dropdown
%button.dropdown-menu-toggle.js-user-search.js-author-search.js-multiselect.js-save-user-data.js-issue-board-sidebar{ type: "button", ref: "assigneeDropdown", data: { toggle: "dropdown", field_name: "issue[assignee_ids][]", first_user: (current_user.username if current_user), current_user: "true", project_id: @project.id, null_user: "true", multi_select: "true", 'max-select' => 1, dropdown: { header: 'Assignee' } },
":data-issuable-id" => "issue.id",
Loading
Loading
Loading
Loading
@@ -32,7 +32,7 @@
 
.selectbox.hide-collapsed
- issuable.assignees.each do |assignee|
= hidden_field_tag "#{issuable.to_ability_name}[assignee_ids][]", assignee.id, id: nil
= hidden_field_tag "#{issuable.to_ability_name}[assignee_ids][]", assignee.id, id: nil, data: { avatar_url: assignee.avatar_url, name: assignee.name, username: assignee.username }
 
- options = { toggle_class: 'js-user-search js-author-search', title: 'Assign to', filter: true, dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author', placeholder: 'Search users', data: { first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), author_id: issuable.author_id, field_name: "#{issuable.to_ability_name}[assignee_ids][]", issue_update: issuable_json_path(issuable), ability_name: issuable.to_ability_name, null_user: true } }
 
Loading
Loading
Loading
Loading
@@ -2,7 +2,7 @@
.col-sm-10{ class: ("col-lg-8" if has_due_date) }
.issuable-form-select-holder.selectbox
- issuable.assignees.each do |assignee|
= hidden_field_tag "#{issuable.to_ability_name}[assignee_ids][]", assignee.id, id: nil, data: { meta: assignee.name }
= hidden_field_tag "#{issuable.to_ability_name}[assignee_ids][]", assignee.id, id: nil, data: { meta: assignee.name, avatar_url: assignee.avatar_url, name: assignee.name, username: assignee.username }
 
- if issuable.assignees.length === 0
= hidden_field_tag "#{issuable.to_ability_name}[assignee_ids][]", 0, id: nil, data: { meta: '' }
Loading
Loading
Loading
Loading
@@ -3,6 +3,7 @@ require 'rails_helper'
describe 'New/edit issue', feature: true, js: true do
include GitlabRoutingHelper
include ActionView::Helpers::JavaScriptHelper
include FormHelper
 
let!(:project) { create(:project) }
let!(:user) { create(:user)}
Loading
Loading
@@ -23,6 +24,65 @@ describe 'New/edit issue', feature: true, js: true do
visit new_namespace_project_issue_path(project.namespace, project)
end
 
describe 'shorten users API pagination limit' do
before do
allow_any_instance_of(FormHelper).to receive(:issue_dropdown_options).and_wrap_original do |original, *args|
has_multiple_assignees = *args[1]
options = {
toggle_class: 'js-user-search js-assignee-search js-multiselect js-save-user-data',
title: 'Select assignee',
filter: true,
dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee',
placeholder: 'Search users',
data: {
per_page: 1,
null_user: true,
current_user: true,
project_id: project.try(:id),
field_name: "issue[assignee_ids][]",
default_label: 'Assignee',
'max-select': 1,
'dropdown-header': 'Assignee',
multi_select: true,
'input-meta': 'name',
'always-show-selectbox': true
}
}
if has_multiple_assignees
options[:title] = 'Select assignee(s)'
options[:data][:'dropdown-header'] = 'Assignee(s)'
options[:data].delete(:'max-select')
end
options
end
visit new_namespace_project_issue_path(project.namespace, project)
click_button 'Unassigned'
wait_for_requests
end
it 'should display selected users even if they are not part of the original API call' do
find('.dropdown-input-field').native.send_keys user2.name
page.within '.dropdown-menu-user' do
expect(page).to have_content user2.name
click_link user2.name
end
find('.js-dropdown-input-clear').click
page.within '.dropdown-menu-user' do
expect(page).to have_content user.name
expect(find('.dropdown-menu-user a.is-active').first(:xpath, '..')['data-user-id']).to eq(user2.id.to_s)
end
end
end
describe 'single assignee' do
before do
click_button 'Unassigned'
Loading
Loading
Loading
Loading
@@ -57,6 +57,23 @@ feature 'Issue Sidebar', feature: true do
expect(page.find('.dropdown-menu-user-link.is-active')).to have_content(user.name)
end
end
it 'keeps your filtered term after filtering and dismissing the dropdown' do
find('.dropdown-input-field').native.send_keys user2.name
wait_for_requests
page.within '.dropdown-menu-user' do
expect(page).not_to have_content 'Unassigned'
click_link user2.name
end
find('.js-right-sidebar').click
find('.block.assignee .edit-link').click
expect(page.all('.dropdown-menu-user li').length).to eq(1)
expect(find('.dropdown-input-field').value).to eq(user2.name)
end
end
 
context 'as a allowed user' do
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment