Commit fbfe0440 authored by Paul Slaughter's avatar Paul Slaughter Committed by Clement Ho

Add vanilla JS avatar_helper and update existing avatar helpers

parent c1fc33d5
import _ from 'underscore';
import { getFirstCharacterCapitalized } from '~/lib/utils/text_utility';
export const DEFAULT_SIZE_CLASS = 's40';
export const IDENTICON_BG_COUNT = 7;
export function getIdenticonBackgroundClass(entityId) {
const type = (entityId % IDENTICON_BG_COUNT) + 1;
return `bg${type}`;
}
export function getIdenticonTitle(entityName) {
return getFirstCharacterCapitalized(entityName) || ' ';
}
export function renderIdenticon(entity, options = {}) {
const { sizeClass = DEFAULT_SIZE_CLASS } = options;
const bgClass = getIdenticonBackgroundClass(entity.id);
const title = getIdenticonTitle(entity.name);
return `<div class="avatar identicon ${_.escape(sizeClass)} ${_.escape(bgClass)}">${_.escape(title)}</div>`;
}
export function renderAvatar(entity, options = {}) {
if (!entity.avatar_url) {
return renderIdenticon(entity, options);
}
const { sizeClass = DEFAULT_SIZE_CLASS } = options;
return `<img src="${_.escape(entity.avatar_url)}" class="avatar ${_.escape(sizeClass)}" />`;
}
...@@ -75,6 +75,20 @@ export function capitalizeFirstCharacter(text) { ...@@ -75,6 +75,20 @@ export function capitalizeFirstCharacter(text) {
return `${text[0].toUpperCase()}${text.slice(1)}`; return `${text[0].toUpperCase()}${text.slice(1)}`;
} }
/**
* Returns the first character capitalized
*
* If falsey, returns empty string.
*
* @param {String} text
* @return {String}
*/
export function getFirstCharacterCapitalized(text) {
return text
? text.charAt(0).toUpperCase()
: '';
}
/** /**
* Replaces all html tags from a string with the given replacement. * Replaces all html tags from a string with the given replacement.
* *
......
<script> <script>
import { getIdenticonBackgroundClass, getIdenticonTitle } from '~/helpers/avatar_helper';
export default { export default {
props: { props: {
entityId: { entityId: {
...@@ -16,26 +18,11 @@ export default { ...@@ -16,26 +18,11 @@ export default {
}, },
}, },
computed: { computed: {
/** identiconBackgroundClass() {
* This method is based on app/helpers/avatars_helper.rb#project_identicon return getIdenticonBackgroundClass(this.entityId);
*/
identiconStyles() {
const allowedColors = [
'#FFEBEE',
'#F3E5F5',
'#E8EAF6',
'#E3F2FD',
'#E0F2F1',
'#FBE9E7',
'#EEEEEE',
];
const backgroundColor = allowedColors[this.entityId % 7];
return `background-color: ${backgroundColor}; color: #555;`;
}, },
identiconTitle() { identiconTitle() {
return this.entityName.charAt(0).toUpperCase(); return getIdenticonTitle(this.entityName);
}, },
}, },
}; };
...@@ -43,8 +30,7 @@ export default { ...@@ -43,8 +30,7 @@ export default {
<template> <template>
<div <div
:class="sizeClass" :class="[sizeClass, identiconBackgroundClass]"
:style="identiconStyles"
class="avatar identicon"> class="avatar identicon">
{{ identiconTitle }} {{ identiconTitle }}
</div> </div>
......
...@@ -69,7 +69,10 @@ ...@@ -69,7 +69,10 @@
.identicon { .identicon {
text-align: center; text-align: center;
vertical-align: top; vertical-align: top;
color: $identicon-fg-color;
background-color: $identicon-gray;
// Sizes
&.s16 { font-size: 12px; line-height: 1.33; } &.s16 { font-size: 12px; line-height: 1.33; }
&.s24 { font-size: 13px; line-height: 1.8; } &.s24 { font-size: 13px; line-height: 1.8; }
&.s26 { font-size: 20px; line-height: 1.33; } &.s26 { font-size: 20px; line-height: 1.33; }
...@@ -82,6 +85,15 @@ ...@@ -82,6 +85,15 @@
&.s110 { font-size: 40px; line-height: 108px; font-weight: $gl-font-weight-normal; } &.s110 { font-size: 40px; line-height: 108px; font-weight: $gl-font-weight-normal; }
&.s140 { font-size: 72px; line-height: 138px; } &.s140 { font-size: 72px; line-height: 138px; }
&.s160 { font-size: 96px; line-height: 158px; } &.s160 { font-size: 96px; line-height: 158px; }
// Background colors
&.bg1 { background-color: $identicon-red; }
&.bg2 { background-color: $identicon-purple; }
&.bg3 { background-color: $identicon-indigo; }
&.bg4 { background-color: $identicon-blue; }
&.bg5 { background-color: $identicon-teal; }
&.bg6 { background-color: $identicon-orange; }
&.bg7 { background-color: $identicon-gray; }
} }
.avatar-container { .avatar-container {
......
...@@ -486,6 +486,18 @@ $note-icon-gutter-width: 55px; ...@@ -486,6 +486,18 @@ $note-icon-gutter-width: 55px;
*/ */
$zen-control-color: #555; $zen-control-color: #555;
/*
* Identicon
*/
$identicon-red: #ffebee;
$identicon-purple: #f3e5f5;
$identicon-indigo: #e8eaf6;
$identicon-blue: #e3f2fd;
$identicon-teal: #e0f2f1;
$identicon-orange: #fbe9e7;
$identicon-gray: $gray-darker;
$identicon-fg-color: #555555;
/* /*
* Calendar * Calendar
*/ */
......
...@@ -15,22 +15,12 @@ module AvatarsHelper ...@@ -15,22 +15,12 @@ module AvatarsHelper
end end
def project_identicon(project, options = {}) def project_identicon(project, options = {})
allowed_colors = { bg_key = (project.id % 7) + 1
red: 'FFEBEE',
purple: 'F3E5F5',
indigo: 'E8EAF6',
blue: 'E3F2FD',
teal: 'E0F2F1',
orange: 'FBE9E7',
gray: 'EEEEEE'
}
options[:class] ||= '' options[:class] ||= ''
options[:class] << ' identicon' options[:class] << ' identicon'
bg_key = project.id % 7 options[:class] << " bg#{bg_key}"
style = "background-color: ##{allowed_colors.values[bg_key]}; color: #555"
content_tag(:div, class: options[:class], style: style) do content_tag(:div, class: options[:class]) do
project.name[0, 1].upcase project.name[0, 1].upcase
end end
end end
......
import { TEST_HOST } from 'spec/test_constants';
import { getFirstCharacterCapitalized } from '~/lib/utils/text_utility';
import {
DEFAULT_SIZE_CLASS,
IDENTICON_BG_COUNT,
renderAvatar,
renderIdenticon,
getIdenticonBackgroundClass,
getIdenticonTitle,
} from '~/helpers/avatar_helper';
function matchAll(str) {
return new RegExp(`^${str}$`);
}
describe('avatar_helper', () => {
describe('getIdenticonBackgroundClass', () => {
it('returns identicon bg class from id', () => {
expect(getIdenticonBackgroundClass(1)).toEqual('bg2');
});
it(`wraps around if id is bigger than ${IDENTICON_BG_COUNT}`, () => {
expect(getIdenticonBackgroundClass(IDENTICON_BG_COUNT + 4)).toEqual('bg5');
expect(getIdenticonBackgroundClass((IDENTICON_BG_COUNT * 5) + 6)).toEqual('bg7');
});
});
describe('getIdenticonTitle', () => {
it('returns identicon title from name', () => {
expect(getIdenticonTitle('Lorem')).toEqual('L');
expect(getIdenticonTitle('dolar-sit-amit')).toEqual('D');
expect(getIdenticonTitle('%-with-special-chars')).toEqual('%');
});
it('returns space if name is falsey', () => {
expect(getIdenticonTitle('')).toEqual(' ');
expect(getIdenticonTitle(null)).toEqual(' ');
});
});
describe('renderIdenticon', () => {
it('renders with the first letter as title and bg based on id', () => {
const entity = {
id: IDENTICON_BG_COUNT + 3,
name: 'Xavior',
};
const options = {
sizeClass: 's32',
};
const result = renderIdenticon(entity, options);
expect(result).toHaveClass(`identicon ${options.sizeClass} bg4`);
expect(result).toHaveText(matchAll(getFirstCharacterCapitalized(entity.name)));
});
it('renders with defaults, if no options are given', () => {
const entity = {
id: 1,
name: 'tanuki',
};
const result = renderIdenticon(entity);
expect(result).toHaveClass(`identicon ${DEFAULT_SIZE_CLASS} bg2`);
expect(result).toHaveText(matchAll(getFirstCharacterCapitalized(entity.name)));
});
});
describe('renderAvatar', () => {
it('renders an image with the avatarUrl', () => {
const avatarUrl = `${TEST_HOST}/not-real-assets/test.png`;
const result = renderAvatar({
avatar_url: avatarUrl,
});
expect(result).toBeMatchedBy('img');
expect(result).toHaveAttr('src', avatarUrl);
expect(result).toHaveClass(DEFAULT_SIZE_CLASS);
});
it('renders an identicon if no avatarUrl', () => {
const entity = {
id: 1,
name: 'walrus',
};
const options = {
sizeClass: 's16',
};
const result = renderAvatar(entity, options);
expect(result).toHaveClass(`identicon ${options.sizeClass} bg2`);
expect(result).toHaveText(matchAll(getFirstCharacterCapitalized(entity.name)));
});
});
});
...@@ -112,4 +112,21 @@ describe('text_utility', () => { ...@@ -112,4 +112,21 @@ describe('text_utility', () => {
expect(textUtils.splitCamelCase('HelloWorld')).toBe('Hello World'); expect(textUtils.splitCamelCase('HelloWorld')).toBe('Hello World');
}); });
}); });
describe('getFirstCharacterCapitalized', () => {
it('returns the first character captialized, if first character is alphabetic', () => {
expect(textUtils.getFirstCharacterCapitalized('loremIpsumDolar')).toEqual('L');
expect(textUtils.getFirstCharacterCapitalized('Sit amit !')).toEqual('S');
});
it('returns the first character, if first character is non-alphabetic', () => {
expect(textUtils.getFirstCharacterCapitalized(' lorem')).toEqual(' ');
expect(textUtils.getFirstCharacterCapitalized('%#!')).toEqual('%');
});
it('returns an empty string, if string is falsey', () => {
expect(textUtils.getFirstCharacterCapitalized('')).toEqual('');
expect(textUtils.getFirstCharacterCapitalized(null)).toEqual('');
});
});
}); });
...@@ -25,19 +25,12 @@ describe('IdenticonComponent', () => { ...@@ -25,19 +25,12 @@ describe('IdenticonComponent', () => {
vm.$destroy(); vm.$destroy();
}); });
describe('identiconStyles', () => { describe('identiconBackgroundClass', () => {
it('should return styles attribute value with `background-color` property', () => { it('should return bg class based on entityId', () => {
vm.entityId = 4; vm.entityId = 4;
expect(vm.identiconStyles).toBeDefined(); expect(vm.identiconBackgroundClass).toBeDefined();
expect(vm.identiconStyles.indexOf('background-color: #E0F2F1;') > -1).toBeTruthy(); expect(vm.identiconBackgroundClass).toBe('bg5');
});
it('should return styles attribute value with `color` property', () => {
vm.entityId = 4;
expect(vm.identiconStyles).toBeDefined();
expect(vm.identiconStyles.indexOf('color: #555;') > -1).toBeTruthy();
}); });
}); });
...@@ -58,7 +51,7 @@ describe('IdenticonComponent', () => { ...@@ -58,7 +51,7 @@ describe('IdenticonComponent', () => {
expect(vm.$el.nodeName).toBe('DIV'); expect(vm.$el.nodeName).toBe('DIV');
expect(vm.$el.classList.contains('identicon')).toBeTruthy(); expect(vm.$el.classList.contains('identicon')).toBeTruthy();
expect(vm.$el.classList.contains('s40')).toBeTruthy(); expect(vm.$el.classList.contains('s40')).toBeTruthy();
expect(vm.$el.getAttribute('style').indexOf('background-color') > -1).toBeTruthy(); expect(vm.$el.classList.contains('bg2')).toBeTruthy();
vm.$destroy(); vm.$destroy();
}); });
......
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