Add vanilla JS avatar_helper and update existing avatar helpers
This commit is contained in:
parent
c1fc33d590
commit
fbfe04401d
|
@ -0,0 +1,33 @@
|
||||||
|
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) {
|
||||||
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.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { getIdenticonBackgroundClass, getIdenticonTitle } from '~/helpers/avatar_helper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
entityId: {
|
entityId: {
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
<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 @@
|
||||||
.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 @@
|
||||||
&.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;
|
||||||
*/
|
*/
|
||||||
$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
|
||||||
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
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
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', () => {
|
||||||
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', () => {
|
||||||
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', () => {
|
||||||
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();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue