Commit 043a67ac authored by Brandon Labuschagne's avatar Brandon Labuschagne Committed by Fatih Acet

UX fixes for projects dropdown filter

parent 1d6db92c
<script>
import { sprintf, n__, __ } from '~/locale';
import { sprintf, n__, s__, __ } from '~/locale';
import $ from 'jquery';
import _ from 'underscore';
import Icon from '~/vue_shared/components/icon.vue';
import { GlLoadingIcon, GlButton } from '@gitlab/ui';
import { GlLoadingIcon, GlButton, GlAvatar } from '@gitlab/ui';
import Api from '~/api';
import { renderAvatar, renderIdenticon } from '~/helpers/avatar_helper';
export default {
name: 'ProjectsDropdownFilter',
......@@ -12,6 +13,7 @@ export default {
Icon,
GlLoadingIcon,
GlButton,
GlAvatar,
},
props: {
groupId: {
......@@ -23,6 +25,11 @@ export default {
required: false,
default: false,
},
label: {
type: String,
required: false,
default: s__('CycleAnalytics|project dropdown filter'),
},
},
data() {
return {
......@@ -46,6 +53,9 @@ export default {
selectedProjectsPlaceholder() {
return this.multiSelect ? __('Select projects') : __('Select a project');
},
isOnlyOneProjectSelected() {
return this.selectedProjects.length === 1;
},
},
mounted() {
$(this.$refs.projectsDropdown).glDropdown({
......@@ -61,6 +71,7 @@ export default {
data: this.fetchData.bind(this),
renderRow: group => this.rowTemplate(group),
text: project => project.name,
opened: e => e.target.querySelector('.dropdown-input-field').focus(),
});
},
methods: {
......@@ -90,11 +101,20 @@ export default {
return `
<li>
<a href='#' class='dropdown-menu-link'>
${_.escape(project.name)}
${this.avatarTemplate(project)}
<div class="align-middle">${_.escape(project.name)}</div>
</a>
</li>
`;
},
avatarTemplate(project) {
const identiconSizeClass = 's16 rect-avatar d-flex justify-content-center flex-column';
return project.avatar_url
? renderAvatar(project, { sizeClass: 's16 rect-avatar' })
: renderIdenticon(project, {
sizeClass: identiconSizeClass,
});
},
},
};
</script>
......@@ -107,7 +127,18 @@ export default {
type="button"
data-toggle="dropdown"
aria-expanded="false"
:aria-label="label"
>
<gl-avatar
v-if="isOnlyOneProjectSelected"
:src="selectedProjects[0].avatar_url"
:entity-id="selectedProjects[0].id"
:entity-name="selectedProjects[0].name"
:size="16"
shape="rect"
:alt="selectedProjects[0].name"
class="prepend-top-2"
/>
{{ selectedProjectsLabel }}
<icon name="chevron-down" />
</gl-button>
......
import { shallowMount } from '@vue/test-utils';
import { mount } from '@vue/test-utils';
import $ from 'jquery';
import 'bootstrap';
import '~/gl_dropdown';
import ProjectsDropdownFilter from 'ee/analytics/shared/components/projects_dropdown_filter.vue';
import Api from '~/api';
import { TEST_HOST } from 'helpers/test_constants';
jest.mock('~/api', () => ({
groupProjects: jest.fn(),
......@@ -13,14 +14,17 @@ const projects = [
{
id: 1,
name: 'foo',
avatar_url: `${TEST_HOST}/images/home/nasa.svg`,
},
{
id: 2,
name: 'foobar',
avatar_url: null,
},
{
id: 3,
name: 'foooooooo',
avatar_url: null,
},
];
......@@ -28,7 +32,7 @@ describe('ProjectsDropdownFilter component', () => {
let wrapper;
const createComponent = (props = {}) => {
wrapper = shallowMount(ProjectsDropdownFilter, {
wrapper = mount(ProjectsDropdownFilter, {
sync: false,
propsData: {
groupId: 1,
......@@ -55,16 +59,55 @@ describe('ProjectsDropdownFilter component', () => {
.trigger('shown.bs.dropdown');
};
const findDropdownItems = () => findDropdown().findAll('a');
const findDropdownButton = () => findDropdown().find('button');
describe('when multiSelect is false', () => {
beforeEach(() => {
createComponent({ multiSelect: false });
});
it('should call glDropdown', () => {
it('calls glDropdown', () => {
expect($.fn.glDropdown).toHaveBeenCalled();
});
describe('displays the correct information', () => {
beforeEach(() => {
openDropdown();
return wrapper.vm.$nextTick();
});
it('contains 3 items', () => {
expect(findDropdownItems().length).toEqual(3);
});
it('renders an avatar when the project has an avatar_url', () => {
expect(
findDropdownItems()
.at(0)
.contains('img.avatar'),
).toBe(true);
expect(
findDropdownItems()
.at(0)
.contains('div.identicon'),
).toBe(false);
});
it("renders an identicon when the project doesn't have an avatar_url", () => {
expect(
findDropdownItems()
.at(1)
.contains('img.avatar'),
).toBe(false);
expect(
findDropdownItems()
.at(1)
.contains('div.identicon'),
).toBe(true);
});
});
describe('on project click', () => {
beforeEach(() => {
openDropdown();
......@@ -97,6 +140,30 @@ describe('ProjectsDropdownFilter component', () => {
},
]);
});
it('renders an avatar in the dropdown button when the project has an avatar_url', done => {
findDropdownItems()
.at(0)
.trigger('click');
wrapper.vm.$nextTick(() => {
expect(findDropdownButton().contains('img.avatar')).toBe(true);
expect(findDropdownButton().contains('div.identicon')).toBe(false);
done();
});
});
it("renders an identicon in the dropdown button when the project doesn't have an avatar_url", done => {
findDropdownItems()
.at(1)
.trigger('click');
wrapper.vm.$nextTick(() => {
expect(findDropdownButton().contains('img.avatar')).toBe(false);
expect(findDropdownButton().contains('div.identicon')).toBe(true);
done();
});
});
});
});
......@@ -105,6 +172,44 @@ describe('ProjectsDropdownFilter component', () => {
createComponent({ multiSelect: true });
});
describe('displays the correct information', () => {
beforeEach(() => {
openDropdown();
return wrapper.vm.$nextTick();
});
it('contains 3 items', () => {
expect(findDropdownItems().length).toEqual(3);
});
it('renders an avatar when the project has an avatar_url', () => {
expect(
findDropdownItems()
.at(0)
.contains('img.avatar'),
).toBe(true);
expect(
findDropdownItems()
.at(0)
.contains('div.identicon'),
).toBe(false);
});
it("renders an identicon when the project doesn't have an avatar_url", () => {
expect(
findDropdownItems()
.at(1)
.contains('img.avatar'),
).toBe(false);
expect(
findDropdownItems()
.at(1)
.contains('div.identicon'),
).toBe(true);
});
});
describe('on project click', () => {
beforeEach(() => {
openDropdown();
......@@ -150,6 +255,21 @@ describe('ProjectsDropdownFilter component', () => {
},
]);
});
it('renders the correct placeholder text when multiple projects are selected', done => {
findDropdownItems()
.at(0)
.trigger('click');
findDropdownItems()
.at(1)
.trigger('click');
wrapper.vm.$nextTick(() => {
expect(findDropdownButton().text()).toBe('2 projects selected');
done();
});
});
});
});
});
......@@ -4368,6 +4368,9 @@ msgstr[1] ""
msgid "CycleAnalytics|group dropdown filter"
msgstr ""
msgid "CycleAnalytics|project dropdown filter"
msgstr ""
msgid "DNS"
msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment