Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFilipa Lacerda <filipa@gitlab.com>2017-08-02 17:36:10 +0300
committerFilipa Lacerda <filipa@gitlab.com>2017-08-02 17:36:10 +0300
commit489a9549421350ce3e67164d7f22aa659608efd6 (patch)
tree1668bc213637438d977e6ff4023b42474e16c6c8
parent315c1ed140c5536edce46940f5f41f81e2680784 (diff)
parentcf9e9fc21ccae9a34c73be3d388c606c9d0f759d (diff)
Merge branch '35408-group-auto-avatars' into 'master'
Show auto-generated avatars in Groups dashboard tree for Groups without avatars See merge request !13188
-rw-r--r--app/assets/javascripts/groups/components/group_identicon.vue45
-rw-r--r--app/assets/javascripts/groups/components/group_item.vue13
-rw-r--r--app/assets/stylesheets/framework/lists.scss4
-rw-r--r--changelogs/unreleased/35408-group-auto-avatars.yml4
-rw-r--r--spec/javascripts/groups/group_identicon_spec.js60
-rw-r--r--spec/javascripts/groups/groups_spec.js13
-rw-r--r--spec/javascripts/groups/mock_data.js4
7 files changed, 141 insertions, 2 deletions
diff --git a/app/assets/javascripts/groups/components/group_identicon.vue b/app/assets/javascripts/groups/components/group_identicon.vue
new file mode 100644
index 00000000000..0edd820743f
--- /dev/null
+++ b/app/assets/javascripts/groups/components/group_identicon.vue
@@ -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>
diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue
index b1db34b9c50..cb133cf7535 100644
--- a/app/assets/javascripts/groups/components/group_item.vue
+++ b/app/assets/javascripts/groups/components/group_item.vue
@@ -1,7 +1,11 @@
<script>
import eventHub from '../event_hub';
+import groupIdenticon from './group_identicon.vue';
export default {
+ components: {
+ groupIdenticon,
+ },
props: {
group: {
type: Object,
@@ -92,6 +96,9 @@ export default {
hasGroups() {
return Object.keys(this.group.subGroups).length > 0;
},
+ hasAvatar() {
+ return this.group.avatarUrl && this.group.avatarUrl.indexOf('/assets/no_group_avatar') === -1;
+ },
},
};
</script>
@@ -194,9 +201,15 @@ export default {
<a
:href="group.groupPath">
<img
+ v-if="hasAvatar"
class="avatar s40"
:src="group.avatarUrl"
/>
+ <group-identicon
+ v-else
+ :entity-id=group.id
+ :entity-name="group.name"
+ />
</a>
</div>
<div
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index 868e65a8f46..ab754f4a492 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -369,6 +369,10 @@ ul.indent-list {
background-color: $row-hover;
cursor: pointer;
}
+
+ .avatar-container > a {
+ width: 100%;
+ }
}
}
diff --git a/changelogs/unreleased/35408-group-auto-avatars.yml b/changelogs/unreleased/35408-group-auto-avatars.yml
new file mode 100644
index 00000000000..77b644a7f94
--- /dev/null
+++ b/changelogs/unreleased/35408-group-auto-avatars.yml
@@ -0,0 +1,4 @@
+---
+title: Show auto-generated avatars for Groups without avatars
+merge_request: 13188
+author:
diff --git a/spec/javascripts/groups/group_identicon_spec.js b/spec/javascripts/groups/group_identicon_spec.js
new file mode 100644
index 00000000000..66772327503
--- /dev/null
+++ b/spec/javascripts/groups/group_identicon_spec.js
@@ -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();
+ });
+ });
+});
diff --git a/spec/javascripts/groups/groups_spec.js b/spec/javascripts/groups/groups_spec.js
index aaffb56fa94..b14153dbbfa 100644
--- a/spec/javascripts/groups/groups_spec.js
+++ b/spec/javascripts/groups/groups_spec.js
@@ -64,6 +64,19 @@ describe('Groups Component', () => {
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', () => {
expect(component.$el.querySelector('#group-12 #group-1128 .title').textContent).toContain('level2 / level3 / level4');
});
diff --git a/spec/javascripts/groups/mock_data.js b/spec/javascripts/groups/mock_data.js
index b3f5d791b89..5bb84b591f4 100644
--- a/spec/javascripts/groups/mock_data.js
+++ b/spec/javascripts/groups/mock_data.js
@@ -1,5 +1,5 @@
const group1 = {
- id: '12',
+ id: 12,
name: 'level1',
path: 'level1',
description: 'foo',
@@ -71,7 +71,7 @@ const group21 = {
path: 'chef',
description: 'foo',
visibility: 'public',
- avatar_url: null,
+ avatar_url: '/uploads/-/system/group/avatar/2/GitLab.png',
web_url: 'http://localhost:3000/groups/devops/chef',
group_path: '/devops/chef',
full_name: 'devops / chef',