Skip to content

Instantly share code, notes, and snippets.

@Gaurav0
Forked from mike-north/components.user-profile.js
Created September 17, 2018 21:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Gaurav0/410425e1676919ced96e7370d4dcdd4d to your computer and use it in GitHub Desktop.
Save Gaurav0/410425e1676919ced96e7370d4dcdd4d to your computer and use it in GitHub Desktop.
LI Bootcamp
import Ember from 'ember';
export default Ember.Component.extend({
classNames: ['profile', 'content-box']
});
import Ember from 'ember';
export default Ember.Controller.extend({
appName: 'Ember Twiddle'
});
import Ember from 'ember';
export function fullName([model]) {
return model.firstName + ' ' + model.lastName;
}
export default Ember.Helper.helper(fullName);
import Ember from 'ember';
export function placeholderUrl(params, hash) {
let width = hash.w;
let height = hash.h;
return `http://placekitten.com/${width}/${height}`;
}
export default Ember.Helper.helper(placeholderUrl);
import Ember from 'ember';
import config from './config/environment';
const Router = Ember.Router.extend({
location: 'none',
rootURL: config.rootURL
});
Router.map(function() {
this.route('profile', {path: 'profile/:id'});
});
export default Router;
import Ember from 'ember';
export default Ember.Route.extend({
model({ id }) {
return {
firstName: 'Reid',
lastName: 'Hoffman',
headline: 'I founded LinkedIn',
};
}
});
body {
padding: 0;
margin: 0;
font-family: Arial, Helvetica, sans-serif;
background-color: rgb(245, 245, 245);
}
body::selection {
background: #0084bf;
color: #fff;
}
nav.navbar {
height: 54px;
background: #283e4a;
margin-bottom: 10px;
}
nav.navbar .navbar-title {
color: white;
display: block;
text-align: center;
padding-top: 7px;
padding-bottom: 7px;
line-height: 40px;
font-size: 30px;
}
.container {
width: 960px;
max-width: calc(100% - 20px);
margin-left: auto;
margin-right: auto;
padding-left: 10px;
padding-right: 10px;
}
/**** utility ****/
.pull-right {
float: right;
}
.pull-left {
float: left;
}
/**** content-box ****/
.content-box {
margin-top: 10px;
border: 1px solid #aaa;
background: #fff;
padding: 10px;
}
.content-box p {
color: #777;
font-weight: 300;
line-height: 1.4rem;
}
/**** Code ****/
code {
margin-left: 2px;
margin-right: 2px;
padding-left: 5px;
padding-right: 5px;
margin-top: 0.1rem;
margin-bottom: 0.1rem;
display: inline-block;
}
pre.code, code {
border-radius: 2px;
background-color: #486F84;
color: #A6F2DB;
line-height: 1.2rem;
}
/**** Profile *****/
.profile .profile-picture {
max-width: 100%;
border-radius: 50%;
border: 1px solid #283e4a;
box-shadow: 0 0 10px rgba(0,0,0,0.8);
display: inline-block;
}
.profile .profile-left {
width: 100px;
float: left;
display: inline-block;
}
.profile .profile-right {
margin-left: 130px;
}
.profile .profile-name {
margin-bottom: 0.5rem;
}
.profile .profile-description,
.profile .profile-location {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
color: #666;
font-weight: 300;
}
.profile .profile-description:before,
.profile .profile-location:before {
margin-right: 10px;
display: inline-block;
width: 20px;
}
.profile .profile-description:before {
content: '📢';
}
.profile .profile-location:before {
content: '🗺';
}
<nav class='navbar'>
<div class='container'>
<span class="navbar-title">Prof.Li</span>
</div>
</nav>
<div class='container'>
{{outlet}}
</div>
<div class="profile-left">
<img src="{{placeholder-url w=403 h=403}}" class="profile-picture">
</div>
<div class="profile-right">
<h1 class="profile-name">{{full-name person}}</h1>
<h3 class="profile-description">{{person.headline}}</h3>
{{!-- Only show location if it's non-empty --}}
{{#if person.location}}
<h3 class="profile-location">{{person.location}}</h3>
{{/if}}
</div>
<div class="content-box">
<h2>Instructions</h2>
<p>
This is a gentle introduction to the UI framework we use at LinkedIn, <a href='http://emberjs.com/' target='_blank'>Ember.js</a>.
</p>
<p>
If you've worked with HTML before, and have a little programming experience, this should be a fun and interesting peek at the kinds of things that front-end engineering across the company do every day!
</p>
</div>
<div class="content-box">
<h3>Welcome!</h3>
<p>
If you check out {{link-to "the screen we've built for a profile" "profile" 1}}, you'll see that we're rendering some user data on the screen. Typically it's the job of a <code>Route</code> to fetch and provide data in the route's model() function.
</p>
<p>
Check out the <code>model()</code> function in the <code>/profile</code> route
<pre class="code">
routes/profile.js
</pre>
</p>
<p>
The value (or eventual value in the case of a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise" target="_blank">Promise</a>), returned by this <code>model</code> function is available in the top-level template associated with the route
<pre class="code">
templates/profile.hbs
</pre>
as a property called <code>model</code>. You can think of these .hbs file as "HTML with extra features" for now. One of these features is the ability to place "bound" values anywhere in the HTML (bound means: whenever the data changes, the HTML will automatically update).
</p>
<p>
Go ahead, try it out by placing
<pre class="code">
&lt;h3&gt;Hello \{{model.firstName}}!&lt;/h3&gt;
</pre>
anywhere in <code> templates/profile.hbs</code>, and then {{link-to "go to the profile page to see how things have updated." "profile" 1}}. Try changing <code>firstName</code> to <code>lastName</code>. Try changing the values in the object returned by the <code>profile.js</code> route's model function, and see how your template is updated in real time
</p>
</div>
<div class="content-box">
<h3>Exercise 1: Components</h3>
<img src="http://i64.tinypic.com/140vvh1.jpg" height=120 class='pull-right'/>
<p>
Components are reusable, self-contained building blocks that we can use to build rich functionality. We have one component in this project already: <code>user-profile</code>. In Ember, we separate the two parts that make up a component: the way it behaves and the way it looks.
</p>
<p>
To describe the way the component looks, we use another handlebars template (hbs file). You can find it at
<pre class="code">
templates/components/user-profile.hbs
</pre>
</p>
<p>
If you try removing the "location" item from the data provided to this component (by modifying <code>routes/profile.js</code>), you should see that it's not rendered at all in the component (even the emjoi to the left of the value has disappeared).
Let's figure out how this is happening, and do the same thing for the <code>headline</code> data.
</p>
</div>
<div class="content-box">
<h3>Exercise 2: Helpers</h3>
<img src="http://i66.tinypic.com/ipva07.png" width=160 class="pull-right"/>
<p>
Helpers are just simple functions that are made available for use in our templates. You've already played with the <code>\{{if}}</code> helper (used to conditionally render something) and we have two other helpers included with this project:
<ul>
<li><code>full-name</code> takes a model and returns the full name corresponding to that user</li>
<li><code>placeholder-url</code> generates an image placeholder URL for us, given a width and height</li>
</ul>
If you click the "Run tests" checkbox on this screen, you should see several failing tests.
<img src="http://i67.tinypic.com/1z35jxs.png" height=40 class="pull-right">
Modify these helpers to make the tests pass.
</p>
</div>
{{link-to "< Back" "index"}}
{{user-profile person=model}}
<div class="content-box">
<h2>Description</h2>
<p>
Meatloaf capicola pork, picanha shoulder pork belly venison pork chop jerky chicken bacon spare ribs flank. Venison porchetta cow alcatra cupim, short loin chicken jerky jowl meatball leberkas. Venison boudin picanha, corned beef bresaola tongue salami ham hock. Kielbasa hamburger t-bone corned beef chuck shankle picanha leberkas swine pork tail frankfurter short ribs. Strip steak cupim rump, short loin boudin bresaola beef ribs drumstick shank burgdoggen chicken hamburger tri-tip swine.
</p>
</div>
import Ember from 'ember';
export default function destroyApp(application) {
Ember.run(application, 'destroy');
}
import Resolver from '../../resolver';
import config from '../../config/environment';
const resolver = Resolver.create();
resolver.namespace = {
modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix
};
export default resolver;
import Ember from 'ember';
import Application from '../../app';
import config from '../../config/environment';
const { run } = Ember;
const assign = Ember.assign || Ember.merge;
export default function startApp(attrs) {
let application;
let attributes = assign({rootElement: "#test-root"}, config.APP);
attributes = assign(attributes, attrs); // use defaults, but you can override;
run(() => {
application = Application.create(attributes);
application.setupForTesting();
application.injectTestHelpers();
});
return application;
}
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('user-profile', 'user-profile component', {
integration: true
});
test('it is rendered as expected when provided with complete data', function(assert) {
this.set('tempModel', {
firstName: 'Reid',
lastName: 'Hoffman',
headline: 'I founded LinkedIn',
location: 'Silicon Valley'
});
this.render(hbs`{{user-profile person=tempModel}}`);
assert.equal(
this.$('.profile-name').text(),
'Reid Hoffman',
'Name shows up as expected'
);
assert.equal(
this.$('.profile-description').text(),
'I founded LinkedIn',
'Headline shows up as expected'
);
assert.equal(
this.$('.profile-location').text(),
'Silicon Valley',
'Location shows up as expected'
);
});
test('When location is missing, it is not rendered', function(assert) {
this.set('tempModel', {
firstName: 'Reid',
lastName: 'Hoffman',
headline: 'I founded LinkedIn'
});
this.render(hbs`{{user-profile person=tempModel}}`);
assert.equal(
this.$('.profile-location').length,
0,
'.profile-location <div> is not in the DOM'
);
});
test('When headline is missing, it is not rendered', function(assert) {
this.set('tempModel', {
firstName: 'Reid',
lastName: 'Hoffman',
location: 'Silicon Valley'
});
this.render(hbs`{{user-profile person=tempModel}}`);
assert.equal(
this.$('.profile-description').text(),
'A LinkedIn Employee',
'In the absence of a "headline" field on the model, "A LinkedIn Employee" is used instead'
);
});
import resolver from './helpers/resolver';
import {
setResolver, start
} from 'ember-qunit';
setResolver(resolver);
start();
import { moduleFor, test } from 'ember-qunit';
import { fullName } from 'twiddle/helpers/full-name';
module('helper:full-name');
test('it works for models with data', function(assert) {
assert.equal(
fullName([{firstName: 'Santa', lastName: 'Claus'}]),
'Santa Claus',
'Correct result for capitalized first and last names');
assert.equal(
fullName([{firstName: 'santa', lastName: 'claus'}]),
'Santa Claus',
'Uncapitalized name parts are capitalized');
assert.equal(
fullName([{firstName: '', lastName: 'Claus'}]),
'Claus',
'Empty first name is ignored (including spaces)');
assert.equal(
fullName([{firstName: 'Santa', lastName: ''}]),
'Santa',
'Empty last name is ignored (including spaces)');
assert.equal(
fullName([{firstName: 'Santa'}]),
'Santa',
'Undefined last name is ignored');
});
test('Provides a reasonable output for unreasonable input', function(assert) {
assert.equal(
fullName([{}]),
'(No Name)',
'"(No Name)" used when firstName and lastName are both empty/undefined');
});
import { moduleFor, test } from 'ember-qunit';
import { placeholderUrl } from 'twiddle/helpers/placeholder-url';
module('helper:placeholder-url');
test('it works when provided with both width and height', function(assert) {
assert.equal(
placeholderUrl([], {w: '200', h: '100'}),
'http://placekitten.com/200/100',
'positive width and height handled properly');
assert.equal(
placeholderUrl([], {w: '-20', h: '100'}),
'http://placekitten.com/40/100',
'If provided with width < 40, uses 40 instead');
assert.equal(
placeholderUrl([], { }),
'http://placekitten.com/40/100',
'If provided with width < 40, uses 40 instead');
});
test('it falls back to defaults when not provided with reasonable width and/or height', function(assert) {
assert.equal(
placeholderUrl([], {}),
'http://placekitten.com/40/40',
'when width and height are unspecified, use 40 x 40');
assert.equal(
placeholderUrl([]),
'http://placekitten.com/40/40',
'If no arguments are provided (helper will receive undefined hash), we still fall back to 40 x 40 size');
});
{
"version": "0.11.1",
"EmberENV": {
"FEATURES": {}
},
"options": {
"use_pods": false,
"enable-testing": true
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js",
"ember": "2.11.0",
"ember-data": "2.11.0",
"ember-template-compiler": "2.11.0",
"ember-testing": "2.11.0"
},
"addons": {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment