Merge branch '34366-issue-sidebar-don-t-render-participants-in-collapsed-state' into 'master'
Resolve "Issue Sidebar : Don't render participants in collapsed state" Closes #34366 See merge request gitlab-org/gitlab-ce!14270
This commit is contained in:
commit
209d7575b6
9 changed files with 173 additions and 70 deletions
|
@ -67,10 +67,13 @@ const PARTICIPANTS_ROW_COUNT = 7;
|
|||
originalText = $(this).data("original-text");
|
||||
if (currentText === originalText) {
|
||||
$(this).text(lessText);
|
||||
|
||||
if (gl.lazyLoader) gl.lazyLoader.loadCheck();
|
||||
} else {
|
||||
$(this).text(originalText);
|
||||
}
|
||||
return $(".js-participants-hidden").toggle();
|
||||
|
||||
$(".js-participants-hidden").toggle();
|
||||
};
|
||||
|
||||
return IssuableContext;
|
||||
|
|
|
@ -29,30 +29,32 @@ import Cookies from 'js-cookie';
|
|||
$('.dropdown').on('loading.gl.dropdown', this.sidebarDropdownLoading);
|
||||
$('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded);
|
||||
|
||||
$document.on('click', '.js-sidebar-toggle', function(e, triggered) {
|
||||
var $allGutterToggleIcons, $this, $thisIcon;
|
||||
e.preventDefault();
|
||||
$this = $(this);
|
||||
$thisIcon = $this.find('i');
|
||||
$allGutterToggleIcons = $('.js-sidebar-toggle i');
|
||||
if ($thisIcon.hasClass('fa-angle-double-right')) {
|
||||
$allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left');
|
||||
$('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
|
||||
$('.page-with-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
|
||||
} else {
|
||||
$allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right');
|
||||
$('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
|
||||
$('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
|
||||
|
||||
if (gl.lazyLoader) gl.lazyLoader.loadCheck();
|
||||
}
|
||||
if (!triggered) {
|
||||
return Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed'));
|
||||
}
|
||||
});
|
||||
$document.on('click', '.js-sidebar-toggle', this.sidebarToggleClicked);
|
||||
return $(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo);
|
||||
};
|
||||
|
||||
Sidebar.prototype.sidebarToggleClicked = function (e, triggered) {
|
||||
var $allGutterToggleIcons, $this, $thisIcon;
|
||||
e.preventDefault();
|
||||
$this = $(this);
|
||||
$thisIcon = $this.find('i');
|
||||
$allGutterToggleIcons = $('.js-sidebar-toggle i');
|
||||
if ($thisIcon.hasClass('fa-angle-double-right')) {
|
||||
$allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left');
|
||||
$('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
|
||||
$('.page-with-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
|
||||
} else {
|
||||
$allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right');
|
||||
$('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
|
||||
$('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
|
||||
|
||||
if (gl.lazyLoader) gl.lazyLoader.loadCheck();
|
||||
}
|
||||
if (!triggered) {
|
||||
Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed'));
|
||||
}
|
||||
};
|
||||
|
||||
Sidebar.prototype.toggleTodo = function(e) {
|
||||
var $btnText, $this, $todoLoading, ajaxType, url;
|
||||
$this = $(e.currentTarget);
|
||||
|
|
|
@ -21,11 +21,14 @@ module ProjectsHelper
|
|||
classes = %W[avatar avatar-inline s#{opts[:size]}]
|
||||
classes << opts[:avatar_class] if opts[:avatar_class]
|
||||
|
||||
image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: classes, alt: '')
|
||||
avatar = avatar_icon(author, opts[:size])
|
||||
src = opts[:lazy_load] ? nil : avatar
|
||||
|
||||
image_tag(src, width: opts[:size], class: classes, alt: '', "data-src" => avatar)
|
||||
end
|
||||
|
||||
def link_to_member(project, author, opts = {}, &block)
|
||||
default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name", tooltip: false }
|
||||
default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name", tooltip: false, lazy_load: false }
|
||||
opts = default_opts.merge(opts)
|
||||
|
||||
return "(deleted)" unless author
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
.hide-collapsed.participants-list
|
||||
- participants.each do |participant|
|
||||
.participants-author.js-participants-author
|
||||
= link_to_member(@project, participant, name: false, size: 24)
|
||||
= link_to_member(@project, participant, name: false, size: 24, lazy_load: true)
|
||||
- if participants_extra > 0
|
||||
.hide-collapsed.participants-more
|
||||
%a.js-participants-more{ href: "#", data: { original_text: "+ #{participants_size - 7} more", less_text: "- show less" } }
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Load sidebar participants avatars only when visible
|
||||
merge_request: 14270
|
||||
author:
|
||||
type: other
|
|
@ -200,13 +200,13 @@ describe ProjectsHelper do
|
|||
end
|
||||
|
||||
it 'returns image tag for member avatar' do
|
||||
expect(helper).to receive(:image_tag).with(expected, { width: 16, class: ["avatar", "avatar-inline", "s16"], alt: "" })
|
||||
expect(helper).to receive(:image_tag).with(expected, { width: 16, class: ["avatar", "avatar-inline", "s16"], alt: "", "data-src" => anything })
|
||||
|
||||
helper.link_to_member_avatar(user)
|
||||
end
|
||||
|
||||
it 'returns image tag with avatar class' do
|
||||
expect(helper).to receive(:image_tag).with(expected, { width: 16, class: ["avatar", "avatar-inline", "s16", "any-avatar-class"], alt: "" })
|
||||
expect(helper).to receive(:image_tag).with(expected, { width: 16, class: ["avatar", "avatar-inline", "s16", "any-avatar-class"], alt: "", "data-src" => anything })
|
||||
|
||||
helper.link_to_member_avatar(user, avatar_class: "any-avatar-class")
|
||||
end
|
||||
|
|
34
spec/javascripts/issuable_context_spec.js
Normal file
34
spec/javascripts/issuable_context_spec.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
/* global IssuableContext */
|
||||
import '~/issuable_context';
|
||||
import $ from 'jquery';
|
||||
|
||||
describe('IssuableContext', () => {
|
||||
describe('toggleHiddenParticipants', () => {
|
||||
const event = jasmine.createSpyObj('event', ['preventDefault']);
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn($.fn, 'data').and.returnValue('data');
|
||||
spyOn($.fn, 'text').and.returnValue('data');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
gl.lazyLoader = undefined;
|
||||
});
|
||||
|
||||
it('calls loadCheck if lazyLoader is set', () => {
|
||||
gl.lazyLoader = jasmine.createSpyObj('lazyLoader', ['loadCheck']);
|
||||
|
||||
IssuableContext.prototype.toggleHiddenParticipants(event);
|
||||
|
||||
expect(gl.lazyLoader.loadCheck).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not throw if lazyLoader is not defined', () => {
|
||||
gl.lazyLoader = undefined;
|
||||
|
||||
const toggle = IssuableContext.prototype.toggleHiddenParticipants.bind(null, event);
|
||||
|
||||
expect(toggle).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -32,56 +32,86 @@ import '~/right_sidebar';
|
|||
};
|
||||
|
||||
describe('RightSidebar', function() {
|
||||
var fixtureName = 'issues/open-issue.html.raw';
|
||||
preloadFixtures(fixtureName);
|
||||
loadJSONFixtures('todos/todos.json');
|
||||
describe('fixture tests', () => {
|
||||
var fixtureName = 'issues/open-issue.html.raw';
|
||||
preloadFixtures(fixtureName);
|
||||
loadJSONFixtures('todos/todos.json');
|
||||
|
||||
beforeEach(function() {
|
||||
loadFixtures(fixtureName);
|
||||
this.sidebar = new Sidebar;
|
||||
$aside = $('.right-sidebar');
|
||||
$page = $('.page-with-sidebar');
|
||||
$icon = $aside.find('i');
|
||||
$toggle = $aside.find('.js-sidebar-toggle');
|
||||
return $labelsIcon = $aside.find('.sidebar-collapsed-icon');
|
||||
});
|
||||
it('should expand/collapse the sidebar when arrow is clicked', function() {
|
||||
assertSidebarState('expanded');
|
||||
$toggle.click();
|
||||
assertSidebarState('collapsed');
|
||||
$toggle.click();
|
||||
assertSidebarState('expanded');
|
||||
});
|
||||
it('should float over the page and when sidebar icons clicked', function() {
|
||||
$labelsIcon.click();
|
||||
return assertSidebarState('expanded');
|
||||
});
|
||||
it('should collapse when the icon arrow clicked while it is floating on page', function() {
|
||||
$labelsIcon.click();
|
||||
assertSidebarState('expanded');
|
||||
$toggle.click();
|
||||
return assertSidebarState('collapsed');
|
||||
});
|
||||
|
||||
it('should broadcast todo:toggle event when add todo clicked', function() {
|
||||
var todos = getJSONFixture('todos/todos.json');
|
||||
spyOn(jQuery, 'ajax').and.callFake(function() {
|
||||
var d = $.Deferred();
|
||||
var response = todos;
|
||||
d.resolve(response);
|
||||
return d.promise();
|
||||
beforeEach(function() {
|
||||
loadFixtures(fixtureName);
|
||||
this.sidebar = new Sidebar;
|
||||
$aside = $('.right-sidebar');
|
||||
$page = $('.page-with-sidebar');
|
||||
$icon = $aside.find('i');
|
||||
$toggle = $aside.find('.js-sidebar-toggle');
|
||||
return $labelsIcon = $aside.find('.sidebar-collapsed-icon');
|
||||
});
|
||||
it('should expand/collapse the sidebar when arrow is clicked', function() {
|
||||
assertSidebarState('expanded');
|
||||
$toggle.click();
|
||||
assertSidebarState('collapsed');
|
||||
$toggle.click();
|
||||
assertSidebarState('expanded');
|
||||
});
|
||||
it('should float over the page and when sidebar icons clicked', function() {
|
||||
$labelsIcon.click();
|
||||
return assertSidebarState('expanded');
|
||||
});
|
||||
it('should collapse when the icon arrow clicked while it is floating on page', function() {
|
||||
$labelsIcon.click();
|
||||
assertSidebarState('expanded');
|
||||
$toggle.click();
|
||||
return assertSidebarState('collapsed');
|
||||
});
|
||||
|
||||
var todoToggleSpy = spyOnEvent(document, 'todo:toggle');
|
||||
it('should broadcast todo:toggle event when add todo clicked', function() {
|
||||
var todos = getJSONFixture('todos/todos.json');
|
||||
spyOn(jQuery, 'ajax').and.callFake(function() {
|
||||
var d = $.Deferred();
|
||||
var response = todos;
|
||||
d.resolve(response);
|
||||
return d.promise();
|
||||
});
|
||||
|
||||
$('.issuable-sidebar-header .js-issuable-todo').click();
|
||||
var todoToggleSpy = spyOnEvent(document, 'todo:toggle');
|
||||
|
||||
expect(todoToggleSpy.calls.count()).toEqual(1);
|
||||
$('.issuable-sidebar-header .js-issuable-todo').click();
|
||||
|
||||
expect(todoToggleSpy.calls.count()).toEqual(1);
|
||||
});
|
||||
|
||||
it('should not hide collapsed icons', () => {
|
||||
[].forEach.call(document.querySelectorAll('.sidebar-collapsed-icon'), (el) => {
|
||||
expect(el.querySelector('.fa, svg').classList.contains('hidden')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not hide collapsed icons', () => {
|
||||
[].forEach.call(document.querySelectorAll('.sidebar-collapsed-icon'), (el) => {
|
||||
expect(el.querySelector('.fa, svg').classList.contains('hidden')).toBeFalsy();
|
||||
describe('sidebarToggleClicked', () => {
|
||||
const event = jasmine.createSpyObj('event', ['preventDefault']);
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn($.fn, 'hasClass').and.returnValue(false);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
gl.lazyLoader = undefined;
|
||||
});
|
||||
|
||||
it('calls loadCheck if lazyLoader is set', () => {
|
||||
gl.lazyLoader = jasmine.createSpyObj('lazyLoader', ['loadCheck']);
|
||||
|
||||
Sidebar.prototype.sidebarToggleClicked(event);
|
||||
|
||||
expect(gl.lazyLoader.loadCheck).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not throw if lazyLoader is not defined', () => {
|
||||
gl.lazyLoader = undefined;
|
||||
|
||||
const toggle = Sidebar.prototype.sidebarToggleClicked.bind(null, event);
|
||||
|
||||
expect(toggle).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
26
spec/views/shared/issuable/_participants.html.haml.rb
Normal file
26
spec/views/shared/issuable/_participants.html.haml.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
require 'spec_helper'
|
||||
require 'nokogiri'
|
||||
|
||||
describe 'shared/issuable/_participants.html.haml' do
|
||||
let(:project) { create(:project) }
|
||||
let(:participants) { create_list(:user, 100) }
|
||||
|
||||
before do
|
||||
allow(view).to receive_messages(project: project,
|
||||
participants: participants)
|
||||
end
|
||||
|
||||
it 'renders lazy loaded avatars' do
|
||||
render 'shared/issuable/participants'
|
||||
|
||||
html = Nokogiri::HTML(rendered)
|
||||
|
||||
avatars = html.css('.participants-author img')
|
||||
|
||||
avatars.each do |avatar|
|
||||
expect(avatar[:class]).to include('lazy')
|
||||
expect(avatar[:src]).to eql(LazyImageTagHelper.placeholder_image)
|
||||
expect(avatar[:"data-src"]).to match('http://www.gravatar.com/avatar/')
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue