Finish changes in #5807 (debounced gravatar load in gh-profile-image)

refs #5807, #5797
- add configurable debounce period
- rename `hasEmail` to `displayGravatar` to better reflect it's purpose
- add tests
This commit is contained in:
Kevin Ansfield 2015-10-26 11:48:38 +00:00
parent de30a0c0ca
commit 2a30f919d9
4 changed files with 126 additions and 75 deletions

View File

@ -5,46 +5,52 @@ import Ember from 'ember';
* A component to manage a user profile image. By default it just handles picture uploads,
* but if passed a bound 'email' property it will render the user's gravatar image
*
* Example: {{gh-profile-image email=controllerEmailProperty setImage="controllerActionName"}}
* Example: {{gh-profile-image email=controllerEmailProperty setImage="controllerActionName" debounce=500}}
*
* @param {int} size The size of the image to render
* @param {String} email Reference to a bound email object if gravatar image behavior is desired.
* @param {String} setImage The string name of the action on the controller to be called when an image is added.
* @property {Boolean} hasUploadedImage Whether or not the user has uploaded an image (whether or not to show the default image/gravatar image)
* @property {String} defaultImage String containing the background-image css property of the default user profile image
* @property {String} imageBackground String containing the background-image css property with the gravatar url
* @param {int} size The size of the image to render
* @param {String} email Reference to a bound email object if gravatar image behavior is desired.
* @param {String|action} setImage The string name of the action on the controller to be called when an image is added.
* @param {int} debounce Period to wait after changes to email before attempting to load gravatar
* @property {Boolean} hasUploadedImage Whether or not the user has uploaded an image (whether or not to show the default image/gravatar image)
* @property {String} defaultImage String containing the background-image css property of the default user profile image
* @property {String} imageBackground String containing the background-image css property with the gravatar url
*/
export default Ember.Component.extend({
email: '',
validEmail: '',
size: 90,
debounce: 300,
validEmail: '',
hasUploadedImage: false,
fileStorage: true,
ghostPaths: Ember.inject.service('ghost-paths'),
hasEmail: Ember.computed.notEmpty('email'),
displayGravatar: Ember.computed.notEmpty('validEmail'),
defaultImage: Ember.computed('ghostPaths', function () {
var url = this.get('ghostPaths.url').asset('/shared/img/user-image.png');
return `background-image: url(${url})`.htmlSafe();
const url = this.get('ghostPaths.url').asset('/shared/img/user-image.png');
return Ember.String.htmlSafe(`background-image: url(${url})`);
}),
trySetValidEmail: function () {
var email = this.get('email');
this.set('validEmail', validator.isEmail(email) ? email : '');
if (!this.get('isDestroyed')) {
const email = this.get('email');
this.set('validEmail', validator.isEmail(email) ? email : '');
}
},
didReceiveAttrs: function () {
Ember.run.debounce(this, 'trySetValidEmail', 500);
didReceiveAttrs: function (attrs) {
const timeout = parseInt(attrs.newAttrs.throttle || this.get('debounce'));
Ember.run.debounce(this, 'trySetValidEmail', timeout);
},
imageBackground: Ember.computed('validEmail', 'size', function () {
var email = this.get('validEmail'),
size = this.get('size'),
url;
const email = this.get('validEmail'),
size = this.get('size');
if (email) {
url = `http://www.gravatar.com/avatar/${md5(email)}?s=${size}&d=blank`;
return `background-image: url(${url})`.htmlSafe();
let url = `http://www.gravatar.com/avatar/${md5(email)}?s=${size}&d=blank`;
return Ember.String.htmlSafe(`background-image: url(${url})`);
}
}),
@ -52,6 +58,9 @@ export default Ember.Component.extend({
var size = this.get('size'),
uploadElement = this.$('.js-file-input');
// Fire this immediately in case we're initialized with a valid email
this.trySetValidEmail();
// while theoretically the 'add' and 'processalways' functions could be
// added as properties of the hash passed to fileupload(), for some reason
// they needed to be placed in an on() call for the add method to work correctly
@ -69,11 +78,13 @@ export default Ember.Component.extend({
},
willDestroyElement: function () {
this.$('.js-file-input').fileupload('destroy');
if (this.$('.js-file-input').data()['blueimp-fileupload']) {
this.$('.js-file-input').fileupload('destroy');
}
},
queueFile: function (e, data) {
var fileName = data.files[0].name;
const fileName = data.files[0].name;
if ((/\.(gif|jpe?g|png|svg?z)$/i).test(fileName)) {
this.sendAction('setImage', data);
@ -81,7 +92,7 @@ export default Ember.Component.extend({
},
triggerPreview: function (e, data) {
var file = data.files[data.index];
const file = data.files[data.index];
if (file.preview) {
this.set('hasUploadedImage', true);
// necessary jQuery code because file.preview is a raw DOM object

View File

@ -2,7 +2,7 @@
{{#unless hasUploadedImage}}
<div class="placeholder-img" style={{defaultImage}}></div>
{{#if hasEmail}}
{{#if displayGravatar}}
<div id="account-image" class="gravatar-img" style={{imageBackground}}>
<span class="sr-only">User image</span>
</div>

View File

@ -0,0 +1,91 @@
/* jshint expr:true */
/* global md5 */
import { expect } from 'chai';
import {
describeComponent,
it
} from 'ember-mocha';
import hbs from 'htmlbars-inline-precompile';
import Ember from 'ember';
const {run} = Ember,
pathsStub = Ember.Service.extend({
url: {
api: function () {
return '';
},
asset: function (src) {
return src;
}
}
});
describeComponent(
'gh-profile-image',
'Integration: Component: gh-profile-image',
{
integration: true
},
function () {
beforeEach(function () {
this.register('service:ghost-paths', pathsStub);
this.inject.service('ghost-paths', {as: 'ghost-paths'});
});
it('renders', function () {
this.set('email', '');
this.render(hbs`
{{gh-profile-image email=email}}
`);
expect(this.$()).to.have.length(1);
});
it('immediately renders the gravatar if valid email supplied', function () {
let email = 'test@example.com',
expectedUrl = `http://www.gravatar.com/avatar/${md5(email)}?s=100&d=blank`;
this.set('email', email);
this.render(hbs`
{{gh-profile-image email=email size=100 debounce=300}}
`);
expect(this.$('.gravatar-img').attr('style'), 'gravatar image style')
.to.equal(`background-image: url(${expectedUrl})`);
});
it('throttles gravatar loading as email is changed', function (done) {
let email = 'test@example.com',
expectedUrl = `http://www.gravatar.com/avatar/${md5(email)}?s=100&d=blank`;
this.set('email', 'test');
this.render(hbs`
{{gh-profile-image email=email size=100 debounce=300}}
`);
expect(this.$('.gravatar-img').length, '.gravatar-img not shown for invalid email')
.to.equal(0);
run(() => {
this.set('email', email);
});
expect(this.$('.gravatar-img').length, '.gravatar-img not immediately changed on email change')
.to.equal(0);
Ember.run.later(this, function () {
expect(this.$('.gravatar-img').length, '.gravatar-img still not shown before throttle timeout')
.to.equal(0);
}, 250);
Ember.run.later(this, function () {
expect(this.$('.gravatar-img').attr('style'), '.gravatar-img style after timeout')
.to.equal(`background-image: url(${expectedUrl})`);
done();
}, 400);
});
}
);

View File

@ -1,51 +0,0 @@
/* jshint expr:true */
/* global md5 */
import { expect } from 'chai';
import {
describeComponent,
it
} from 'ember-mocha';
describeComponent(
'gh-profile-image',
'Unit: Component: gh-profile-image',
{
unit: true,
needs: ['service:ghost-paths']
},
function () {
it('renders', function () {
// creates the component instance
var component = this.subject();
expect(component._state).to.equal('preRender');
// renders the component on the page
this.render();
expect(component._state).to.equal('inDOM');
});
it('renders the gravatar image background if email is supplied', function () {
var component = this.subject(),
testEmail = 'test@example.com',
style, size;
Ember.run(function () {
component.set('email', testEmail);
});
this.render();
size = component.get('size');
style = 'url(http://www.gravatar.com/avatar/' + md5(testEmail) + '?s=' + size + '&d=blank)';
expect(component.$('#account-image').css('background-image')).to.equal(style);
});
it('doesn\'t render the gravatar image background if email isn\'t supplied', function () {
var component = this.subject();
this.render();
expect(component.$('#account-image').length).to.equal(0);
});
}
);