Merge branch '35408-group-auto-avatars' into 'master'

Show auto-generated avatars in Groups dashboard tree for Groups without avatars

See merge request !13188
This commit is contained in:
Filipa Lacerda 2017-08-02 14:36:10 +00:00
commit 489a954942
7 changed files with 141 additions and 2 deletions

View file

@ -0,0 +1,45 @@
<script>
export default {
props: {
entityId: {
type: Number,
required: true,
},
entityName: {
type: String,
required: true,
},
},
computed: {
/**
* This method is based on app/helpers/application_helper.rb#project_identicon
*/
identiconStyles() {
const allowedColors = [
'#FFEBEE',
'#F3E5F5',
'#E8EAF6',
'#E3F2FD',
'#E0F2F1',
'#FBE9E7',
'#EEEEEE',
];
const backgroundColor = allowedColors[this.entityId % 7];
return `background-color: ${backgroundColor}; color: #555;`;
},
identiconTitle() {
return this.entityName.charAt(0).toUpperCase();
},
},
};
</script>
<template>
<div
class="avatar s40 identicon"
:style="identiconStyles">
{{identiconTitle}}
</div>
</template>

View file

@ -1,7 +1,11 @@
<script> <script>
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import groupIdenticon from './group_identicon.vue';
export default { export default {
components: {
groupIdenticon,
},
props: { props: {
group: { group: {
type: Object, type: Object,
@ -92,6 +96,9 @@ export default {
hasGroups() { hasGroups() {
return Object.keys(this.group.subGroups).length > 0; return Object.keys(this.group.subGroups).length > 0;
}, },
hasAvatar() {
return this.group.avatarUrl && this.group.avatarUrl.indexOf('/assets/no_group_avatar') === -1;
},
}, },
}; };
</script> </script>
@ -194,9 +201,15 @@ export default {
<a <a
:href="group.groupPath"> :href="group.groupPath">
<img <img
v-if="hasAvatar"
class="avatar s40" class="avatar s40"
:src="group.avatarUrl" :src="group.avatarUrl"
/> />
<group-identicon
v-else
:entity-id=group.id
:entity-name="group.name"
/>
</a> </a>
</div> </div>
<div <div

View file

@ -369,6 +369,10 @@ ul.indent-list {
background-color: $row-hover; background-color: $row-hover;
cursor: pointer; cursor: pointer;
} }
.avatar-container > a {
width: 100%;
}
} }
} }

View file

@ -0,0 +1,4 @@
---
title: Show auto-generated avatars for Groups without avatars
merge_request: 13188
author:

View file

@ -0,0 +1,60 @@
import Vue from 'vue';
import groupIdenticonComponent from '~/groups/components/group_identicon.vue';
import GroupsStore from '~/groups/stores/groups_store';
import { group1 } from './mock_data';
const createComponent = () => {
const Component = Vue.extend(groupIdenticonComponent);
const store = new GroupsStore();
const group = store.decorateGroup(group1);
return new Component({
propsData: {
entityId: group.id,
entityName: group.name,
},
}).$mount();
};
describe('GroupIdenticonComponent', () => {
let vm;
beforeEach(() => {
vm = createComponent();
});
describe('computed', () => {
describe('identiconStyles', () => {
it('should return styles attribute value with `background-color` property', () => {
vm.entityId = 4;
expect(vm.identiconStyles).toBeDefined();
expect(vm.identiconStyles.indexOf('background-color: #E0F2F1;') > -1).toBeTruthy();
});
it('should return styles attribute value with `color` property', () => {
vm.entityId = 4;
expect(vm.identiconStyles).toBeDefined();
expect(vm.identiconStyles.indexOf('color: #555;') > -1).toBeTruthy();
});
});
describe('identiconTitle', () => {
it('should return first letter of entity title in uppercase', () => {
vm.entityName = 'dummy-group';
expect(vm.identiconTitle).toBeDefined();
expect(vm.identiconTitle).toBe('D');
});
});
});
describe('template', () => {
it('should render identicon', () => {
expect(vm.$el.nodeName).toBe('DIV');
expect(vm.$el.classList.contains('identicon')).toBeTruthy();
expect(vm.$el.getAttribute('style').indexOf('background-color') > -1).toBeTruthy();
});
});
});

View file

@ -64,6 +64,19 @@ describe('Groups Component', () => {
expect(lists[2].querySelector('#group-1120').textContent).toContain(groups.id1119.subGroups.id1120.name); expect(lists[2].querySelector('#group-1120').textContent).toContain(groups.id1119.subGroups.id1120.name);
}); });
it('should render group identicon when group avatar is not present', () => {
const avatar = component.$el.querySelector('#group-12 .avatar-container .avatar');
expect(avatar.nodeName).toBe('DIV');
expect(avatar.classList.contains('identicon')).toBeTruthy();
expect(avatar.getAttribute('style').indexOf('background-color') > -1).toBeTruthy();
});
it('should render group avatar when group avatar is present', () => {
const avatar = component.$el.querySelector('#group-1120 .avatar-container .avatar');
expect(avatar.nodeName).toBe('IMG');
expect(avatar.classList.contains('identicon')).toBeFalsy();
});
it('should remove prefix of parent group', () => { it('should remove prefix of parent group', () => {
expect(component.$el.querySelector('#group-12 #group-1128 .title').textContent).toContain('level2 / level3 / level4'); expect(component.$el.querySelector('#group-12 #group-1128 .title').textContent).toContain('level2 / level3 / level4');
}); });

View file

@ -1,5 +1,5 @@
const group1 = { const group1 = {
id: '12', id: 12,
name: 'level1', name: 'level1',
path: 'level1', path: 'level1',
description: 'foo', description: 'foo',
@ -71,7 +71,7 @@ const group21 = {
path: 'chef', path: 'chef',
description: 'foo', description: 'foo',
visibility: 'public', visibility: 'public',
avatar_url: null, avatar_url: '/uploads/-/system/group/avatar/2/GitLab.png',
web_url: 'http://localhost:3000/groups/devops/chef', web_url: 'http://localhost:3000/groups/devops/chef',
group_path: '/devops/chef', group_path: '/devops/chef',
full_name: 'devops / chef', full_name: 'devops / chef',