Skip to content

Commit

Permalink
Merge pull request #788 from WenInCode/add-volunteer-headshot-component
Browse files Browse the repository at this point in the history
create volunteer-headshot component
  • Loading branch information
joshsmith committed Mar 29, 2017
2 parents c8fd6c8 + dcbb7e9 commit 5488f92
Show file tree
Hide file tree
Showing 12 changed files with 332 additions and 8 deletions.
76 changes: 75 additions & 1 deletion app/components/thank-you-container.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,85 @@ import Ember from 'ember';

const {
Component,
computed,
computed: { filter, mapBy },
get,
inject: { service }
} = Ember;

const MAX_VOLUNTEERS = 12;

/**
The `thank-you-container` component presents the main content for the
`thank-you` donations page. This includes an icon, thank you message and
a list of contributors.
## Default Usage
```handlebars
{{thank-you-container project=project}}
```
@class thank-you-container
@module Component
@extends Ember.Component
*/
export default Component.extend({
classNames: ['thank-you-container'],

onboarding: service()
onboarding: service(),

/**
A filter for the project's approved users
@property approvedProjectUsers
@type Ember.Array
*/
approvedProjectUsers: filter('project.projectUsers', function(projectUser) {
return get(projectUser, 'role') !== 'pending';
}),

/**
A computed array of approved members
@property approvedUsers
@type Ember.Array
*/
approvedUsers: mapBy('approvedProjectUsers', 'user'),

/**
Retuns a subset of at most `MAX_VOLUNTEERS` members from the `approvedUsers` array.
@property volunteers
@type Ember.Array
*/
volunteers: computed('approvedUsers', function() {
let approvedUsers = get(this, 'approvedUsers');

if (approvedUsers.length > MAX_VOLUNTEERS) {
approvedUsers = this.randomSubset(approvedUsers, MAX_VOLUNTEERS);
}

return approvedUsers;
}),

/*
* Source: http://stackoverflow.com/a/11935263/1787262
* Username: Tim Down
* Date: December 3rd, 2016
*/
randomSubset(arr, size) {
let shuffled = arr.slice(0);
let i = arr.length;
let temp, index;

while (i--) {
index = Math.floor((i + 1) * Math.random());
temp = shuffled[index];
shuffled[index] = shuffled[i];
shuffled[i] = temp;
}

return shuffled.slice(0, size);
}
});
74 changes: 74 additions & 0 deletions app/components/volunteer-headshot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import Ember from 'ember';

const {
Component,
computed,
get,
isPresent
} = Ember;

/**
The `volunteer-headshot` component presents a thumbnail of a volunteer, their
name and randomly selects one of their roles.
## Default Usage
```handlebars
{{volunteer-headshot volunteer=user}}
```
@class volunteer-headshot
@module Component
@extends Ember.Component
*/
export default Component.extend({
attributeBindings: ['data-test-selector'],
classNames: ['volunteer-headshot'],

/**
A computed alias of the volunteer's user roles.
@property userRoles
@type Ember.Array
*/
userRoles: computed.alias('volunteer.userRoles'),

/**
A randomly selected role from the `userRoles` property.
@property userRole
@type Ember.Model
*/
userRole: computed('userRoles', function() {
let userRoles = get(this, 'userRoles');

if (isPresent(userRoles)) {
let randomIndex = Math.floor(Math.random() * get(userRoles, 'length'));

return userRoles.objectAt(randomIndex);
}
}),

/**
Returns the volunteer's name. If the `name` property is not defined, it
computes a name from the volunteer's `firstName` & `lastName`. If neither
are defined it returns the volunteer's username.
@property volunteerName
@type String
*/
volunteerName: computed('volunteer.{name,firstName,lastName}', function() {
let name = get(this, 'volunteer.name');
let firstName = get(this, 'volunteer.firstName');
let lastName = get(this, 'volunteer.lastName');
let userName = get(this, 'volunteer.userName');

if (isPresent(name)) {
return name;
} else if (isPresent(firstName) && isPresent(lastName)) {
return `${firstName} ${lastName}`;
} else {
return userName;
}
})
});
19 changes: 19 additions & 0 deletions app/models/organization-membership.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
import { belongsTo } from 'ember-data/relationships';
import Ember from 'ember';

const { computed } = Ember;

export default Model.extend({
role: attr(),

member: belongsTo('user', { async: true }),
organization: belongsTo('organization', { async: true }),

isAdmin: computed.equal('role', 'admin'),
isContributor: computed.equal('role', 'contributor'),
isNotPending: computed.not('isPending'),
isOwner: computed.equal('role', 'owner'),
isPending: computed.equal('role', 'pending')
});
1 change: 1 addition & 0 deletions app/styles/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@

@import "components/user-projects-list";
@import "components/user-sidebar";
@import "components/volunteer-headshot";

// used from task/new.hbs
@import "components/project-skills-list";
Expand Down
22 changes: 22 additions & 0 deletions app/styles/components/volunteer-headshot.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.volunteer-headshot {
@include omega(6n);
@include span-columns(2);

display: inline-block;
margin-bottom: 1em;

&__name {
font-weight: 600;
margin: 0;
}

&__role {
font-size: $body-font-size-small;
color: $text--light;
margin-top: 0;
}

img {
border-radius: 4px;
}
}
18 changes: 14 additions & 4 deletions app/styles/templates/project/thank-you.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,27 @@
}
}

&__contributors {
@include span-columns(12);

display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
margin: 1.4em 0;
}

h3 {
font-size: $header-font-size-large;
font-weight: 600;
margin: 10px 0;
}

p {
margin-bottom: 1.4em;
}

&__message {
color: $text--light;

p {
margin-bottom: 1.4em;
}
}
}
5 changes: 5 additions & 0 deletions app/templates/components/thank-you-container.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@
{{/if}}
</p>
</div>
<div class="thank-you-container__contributors">
{{#each volunteers as |volunteer|}}
{{volunteer-headshot volunteer=volunteer data-test-selector="volunteer headshot"}}
{{/each}}
</div>
3 changes: 3 additions & 0 deletions app/templates/components/volunteer-headshot.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<img class="volunteer-headshot__image icon large" src={{volunteer.photoThumbUrl}} alt={{volunteerName}} />
<p class="volunteer-headshot__name" data-test-selector="volunteer name">{{volunteerName}}</p>
<p class="volunteer-headshot__role" data-test-selector="volunteer role">{{userRole.role.name}}</p>
39 changes: 37 additions & 2 deletions tests/integration/components/thank-you-container-test.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,45 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import PageObject from 'ember-cli-page-object';
import thankYouContainerComponent from '../../pages/components/thank-you-container';
import thankYouContainerComponent from 'code-corps-ember/tests/pages/components/thank-you-container';

let page = PageObject.create(thankYouContainerComponent);

const members = [
'Rudolph',
'Charlie Brown',
'Yukon Cornelius',
'Frosty the Snowman'
];

function generateProjectUsers(size) {
let projectUsers = [];

for (let i = 0; i < size; i++) {
projectUsers.push({
role: 'contributor',
user: {
name: members[Math.floor(Math.random() * members.length)],
photoThumbUrl: '/assets/images/icons/test.png',
userRoles: [
{
name: 'Contributor'
}
]
}
});
}

return projectUsers;
}

moduleForComponent('thank-you-container', 'Integration | Component | thank-you container', {
integration: true,
beforeEach() {
this.set('project', {
id: 42,
title: 'A Test Project'
title: 'A Test Project',
projectUsers: generateProjectUsers(14)
});

page.setContext(this);
Expand All @@ -23,3 +52,9 @@ test('it renders the thank you text', function(assert) {

assert.equal(page.thankYouText, `From all the volunteers on the ${this.get('project.title')} team.`);
});

test('it renders a subset of 12 volunteers', function(assert) {
assert.expect(1);

assert.equal(page.volunteers().count, 12);
});
63 changes: 63 additions & 0 deletions tests/integration/components/volunteer-headshot-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import PageObject from 'ember-cli-page-object';
import volunteerHeadshot from '../../pages/components/volunteer-headshot';

let page = PageObject.create(volunteerHeadshot);

const userRoles = [
{ role: { name: 'Developer' } },
{ role: { name: 'Ember Developer' } },
{ role: { name: 'UX Designer' } },
{ role: { name: 'Software Engineer' } },
{ role: { name: 'Project Coordinator' } },
{ role: { name: 'Designer & Developer' } }
];

moduleForComponent('volunteer-headshot', 'Integration | Component | volunteer headshot', {
integration: true,
beforeEach() {
this.set('user', {
name: 'Test User',
photoThumbUrl: '/assets/images/icons/test.png',
userRoles
});
page.setContext(this);
page.render(hbs`{{volunteer-headshot volunteer=user}}`);
}
});

test('it renders the volunteer\'s name', function(assert) {
assert.expect(1);
assert.equal(page.name, this.get('user.name'));
});

test('it computes the name if it is not present', function(assert) {
let firstName = 'Split';
let lastName = 'Name';

this.set('user.name', null);
this.set('user.firstName', firstName);
this.set('user.lastName', lastName);

assert.equal(page.name, `${firstName} ${lastName}`);
});

test('it randomly selects one of the available roles', function(assert) {
assert.expect(1);

let roles = userRoles.map((userRole) => {
return userRole.role.name;
});
assert.ok(roles.includes(page.role));
});

test('it sets the image alt text', function(assert) {
assert.expect(1);
assert.equal(page.image.alt, `${this.get('user.name')}`);
});

test('it sets the image src', function(assert) {
assert.expect(1);
assert.equal(page.image.src, this.get('user.photoThumbUrl'));
});
7 changes: 6 additions & 1 deletion tests/pages/components/thank-you-container.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
clickable,
collection,
text
} from 'ember-cli-page-object';

Expand All @@ -12,5 +13,9 @@ export default {

clickLink: clickable('a'),

thankYouText: text('[data-test-selector="thank you message"]')
thankYouText: text('[data-test-selector="thank you message"]'),

volunteers: collection({
scope: '[data-test-selector="volunteer headshot"]'
})
};
Loading

0 comments on commit 5488f92

Please sign in to comment.