Skip to content
Snippets Groups Projects
Commit f2b34fb2 authored by Winnie Hellmann's avatar Winnie Hellmann Committed by Phil Hughes
Browse files

Show current user immediately in issuable filters

parent 5cb8ad6c
No related branches found
No related tags found
No related merge requests found
Showing with 266 additions and 22 deletions
Loading
Loading
@@ -8,7 +8,7 @@ const Keyboard = function () {
var isUpArrow = false;
var isDownArrow = false;
var removeHighlight = function removeHighlight(list) {
var itemElements = Array.prototype.slice.call(list.list.querySelectorAll('li:not(.divider)'), 0);
var itemElements = Array.prototype.slice.call(list.list.querySelectorAll('li:not(.divider):not(.hidden)'), 0);
var listItems = [];
for(var i = 0; i < itemElements.length; i++) {
var listItem = itemElements[i];
Loading
Loading
Loading
Loading
@@ -63,6 +63,9 @@ const AjaxFilter = {
return AjaxCache.retrieve(url)
.then((data) => {
this._loadData(data, config);
if (config.onLoadingFinished) {
config.onLoadingFinished(data);
}
})
.catch(config.onError);
},
Loading
Loading
Loading
Loading
@@ -18,6 +18,9 @@ class DropdownUser extends gl.FilteredSearchDropdown {
},
searchValueFunction: this.getSearchInput.bind(this),
loadingTemplate: this.loadingTemplate,
onLoadingFinished: () => {
this.hideCurrentUser();
},
onError() {
/* eslint-disable no-new */
new Flash('An error occured fetching the dropdown data.');
Loading
Loading
@@ -28,6 +31,11 @@ class DropdownUser extends gl.FilteredSearchDropdown {
this.tokenKeys = tokenKeys;
}
 
hideCurrentUser() {
const currenUserItem = this.dropdown.querySelector('.js-current-user');
currenUserItem.classList.add('hidden');
}
itemClicked(e) {
super.itemClicked(e,
selected => selected.querySelector('.dropdown-light-content').innerText.trim());
Loading
Loading
Loading
Loading
@@ -8,18 +8,28 @@ module AvatarsHelper
}))
end
 
def user_avatar(options = {})
def user_avatar_without_link(options = {})
avatar_size = options[:size] || 16
user_name = options[:user].try(:name) || options[:user_name]
css_class = options[:css_class] || ''
avatar = image_tag(
avatar_icon(options[:user] || options[:user_email], avatar_size),
avatar_url = options[:url] || avatar_icon(options[:user] || options[:user_email], avatar_size)
data_attributes = { container: 'body' }
if options[:lazy]
data_attributes[:src] = avatar_url
end
image_tag(
options[:lazy] ? '' : avatar_url,
class: "avatar has-tooltip s#{avatar_size} #{css_class}",
alt: "#{user_name}'s avatar",
title: user_name,
data: { container: 'body' }
data: data_attributes
)
end
def user_avatar(options = {})
avatar = user_avatar_without_link(options)
 
if options[:user]
link_to(avatar, user_path(options[:user]))
Loading
Loading
Loading
Loading
@@ -46,30 +46,27 @@
%span.js-filter-tag.dropdown-light-content
{{tag}}
#js-dropdown-author.filtered-search-input-dropdown-menu.dropdown-menu
- if current_user
%ul{ data: { dropdown: true } }
= render 'shared/issuable/user_dropdown_item',
user: current_user
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item
%button.btn.btn-link.dropdown-user
%img.avatar{ alt: '{{name}}\'s avatar', width: '30', data: { src: '{{avatar_url}}' } }
.dropdown-user-details
%span
{{name}}
%span.dropdown-light-content
@{{username}}
= render 'shared/issuable/user_dropdown_item',
user: User.new(username: '{{username}}', name: '{{name}}'),
avatar: { lazy: true, url: '{{avatar_url}}' }
#js-dropdown-assignee.filtered-search-input-dropdown-menu.dropdown-menu
%ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { value: 'none' } }
%button.btn.btn-link
No Assignee
%li.divider
- if current_user
= render 'shared/issuable/user_dropdown_item',
user: current_user
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item
%button.btn.btn-link.dropdown-user
%img.avatar{ alt: '{{name}}\'s avatar', width: '30', data: { src: '{{avatar_url}}' } }
.dropdown-user-details
%span
{{name}}
%span.dropdown-light-content
@{{username}}
= render 'shared/issuable/user_dropdown_item',
user: User.new(username: '{{username}}', name: '{{name}}'),
avatar: { lazy: true, url: '{{avatar_url}}' }
#js-dropdown-milestone.filtered-search-input-dropdown-menu.dropdown-menu
%ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { value: 'none' } }
Loading
Loading
- user = local_assigns.fetch(:user)
- avatar = local_assigns.fetch(:avatar, { })
%li.filter-dropdown-item{ class: ('js-current-user' if user == current_user) }
%button.btn.btn-link.dropdown-user{ type: :button }
= user_avatar_without_link(user: user, lazy: avatar[:lazy], url: avatar[:url], size: 30)
.dropdown-user-details
%span
= user.name
%span.dropdown-light-content
= user.to_reference
---
title: Show current user immediately in issuable filters
merge_request: 11630
author:
Loading
Loading
@@ -157,6 +157,25 @@ describe 'Dropdown assignee', :feature, :js do
end
end
 
describe 'selecting from dropdown without Ajax call' do
before do
Gitlab::Testing::RequestBlockerMiddleware.block_requests!
filtered_search.set('assignee:')
end
after do
Gitlab::Testing::RequestBlockerMiddleware.allow_requests!
end
it 'selects current user' do
find('#js-dropdown-assignee .filter-dropdown-item', text: user.username).click
expect(page).to have_css(js_dropdown_assignee, visible: false)
expect_tokens([{ name: 'assignee', value: user.username }])
expect_filtered_search_input_empty
end
end
describe 'input has existing content' do
it 'opens assignee dropdown with existing search term' do
filtered_search.set('searchTerm assignee:')
Loading
Loading
Loading
Loading
@@ -135,6 +135,25 @@ describe 'Dropdown author', js: true, feature: true do
end
end
 
describe 'selecting from dropdown without Ajax call' do
before do
Gitlab::Testing::RequestBlockerMiddleware.block_requests!
filtered_search.set('author:')
end
after do
Gitlab::Testing::RequestBlockerMiddleware.allow_requests!
end
it 'selects current user' do
find('#js-dropdown-author .filter-dropdown-item', text: user.username).click
expect(page).to have_css(js_dropdown_author, visible: false)
expect_tokens([{ name: 'author', value: user.username }])
expect_filtered_search_input_empty
end
end
describe 'input has existing content' do
it 'opens author dropdown with existing search term' do
filtered_search.set('searchTerm author:')
Loading
Loading
require 'rails_helper'
 
describe AvatarsHelper do
include ApplicationHelper
let(:user) { create(:user) }
 
describe '#user_avatar' do
Loading
Loading
@@ -18,4 +20,103 @@ describe AvatarsHelper do
is_expected.to include(CGI.escapeHTML(user.avatar_url(size: 16)))
end
end
describe '#user_avatar_without_link' do
let(:options) { { user: user } }
subject { helper.user_avatar_without_link(options) }
it 'displays user avatar' do
is_expected.to eq image_tag(
avatar_icon(user, 16),
class: 'avatar has-tooltip s16 ',
alt: "#{user.name}'s avatar",
title: user.name,
data: { container: 'body' }
)
end
context 'with css_class parameter' do
let(:options) { { user: user, css_class: '.cat-pics' } }
it 'uses provided css_class' do
is_expected.to eq image_tag(
avatar_icon(user, 16),
class: "avatar has-tooltip s16 #{options[:css_class]}",
alt: "#{user.name}'s avatar",
title: user.name,
data: { container: 'body' }
)
end
end
context 'with lazy parameter' do
let(:options) { { user: user, lazy: true } }
it 'uses data-src instead of src' do
is_expected.to eq image_tag(
'',
class: 'avatar has-tooltip s16 ',
alt: "#{user.name}'s avatar",
title: user.name,
data: { container: 'body', src: avatar_icon(user, 16) }
)
end
end
context 'with size parameter' do
let(:options) { { user: user, size: 99 } }
it 'uses provided size' do
is_expected.to eq image_tag(
avatar_icon(user, options[:size]),
class: "avatar has-tooltip s#{options[:size]} ",
alt: "#{user.name}'s avatar",
title: user.name,
data: { container: 'body' }
)
end
end
context 'with url parameter' do
let(:options) { { user: user, url: '/over/the/rainbow.png' } }
it 'uses provided url' do
is_expected.to eq image_tag(
options[:url],
class: 'avatar has-tooltip s16 ',
alt: "#{user.name}'s avatar",
title: user.name,
data: { container: 'body' }
)
end
end
context 'with user_name parameter' do
let(:options) { { user_name: 'Tinky Winky', user_email: 'no@f.un' } }
context 'with user parameter' do
let(:options) { { user: user, user_name: 'Tinky Winky' } }
it 'prefers user parameter' do
is_expected.to eq image_tag(
avatar_icon(user, 16),
class: 'avatar has-tooltip s16 ',
alt: "#{user.name}'s avatar",
title: user.name,
data: { container: 'body' }
)
end
end
it 'uses user_name and user_email parameter if user is not present' do
is_expected.to eq image_tag(
avatar_icon(options[:user_email], 16),
class: 'avatar has-tooltip s16 ',
alt: "#{options[:user_name]}'s avatar",
title: options[:user_name],
data: { container: 'body' }
)
end
end
end
end
import AjaxCache from '~/lib/utils/ajax_cache';
import AjaxFilter from '~/droplab/plugins/ajax_filter';
describe('AjaxFilter', () => {
let dummyConfig;
const dummyData = 'dummy data';
let dummyList;
beforeEach(() => {
dummyConfig = {
endpoint: 'dummy endpoint',
searchKey: 'dummy search key',
};
dummyList = {
data: [],
list: document.createElement('div'),
};
AjaxFilter.hook = {
config: {
AjaxFilter: dummyConfig,
},
list: dummyList,
};
});
describe('trigger', () => {
let ajaxSpy;
beforeEach(() => {
spyOn(AjaxCache, 'retrieve').and.callFake(url => ajaxSpy(url));
spyOn(AjaxFilter, '_loadData');
dummyConfig.onLoadingFinished = jasmine.createSpy('spy');
const dynamicList = document.createElement('div');
dynamicList.dataset.dynamic = true;
dummyList.list.appendChild(dynamicList);
});
it('calls onLoadingFinished after loading data', (done) => {
ajaxSpy = (url) => {
expect(url).toBe('dummy endpoint?dummy search key=');
return Promise.resolve(dummyData);
};
AjaxFilter.trigger()
.then(() => {
expect(dummyConfig.onLoadingFinished.calls.count()).toBe(1);
})
.then(done)
.catch(done.fail);
});
it('does not call onLoadingFinished if Ajax call fails', (done) => {
const dummyError = new Error('My dummy is sick! :-(');
ajaxSpy = (url) => {
expect(url).toBe('dummy endpoint?dummy search key=');
return Promise.reject(dummyError);
};
AjaxFilter.trigger()
.then(done.fail)
.catch((error) => {
expect(error).toBe(dummyError);
expect(dummyConfig.onLoadingFinished.calls.count()).toBe(0);
})
.then(done)
.catch(done.fail);
});
});
});
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