Ember JS: How to import Material Components Web JS - ember.js

I'm trying to use Material Components Web(MDC-Web) with an EmberJS application. I've installed material-components-web with Yarn.
yarn add material-components-web --dev
This installed material-components-web#0.41.0.
I have the sass/CSS working but can't figure out how to include/import the JS. Could someone show me how it would import that into my component js file and/or my component template file.
I get an no file error when I do an
app.import('node_modules/material-components-web/dist/material-components-web.js')
Thanks for any help!

This is how I normally use Material Components Web in my Ember project. Has worked for me since Ember version 2.x upto the latest 3.x.
First in ember-cli-build.js. Import the required js file
app.import('node_modules/material-components-web/dist/material-components-web.js', {
using: [{
transformation: 'fastbootShim'
}]
});
The using part is only applicable if you use Fastboot (which you should) so as to exclude the file from executing in a Fastboot environment.
Then fear not, in an Ember component on the didInsertElement hook, activate the MDC component for example for a modal drawer component I have used code like this.
tagName: 'aside',
classNames: ['mdc-drawer', 'mdc-drawer--modal', 'app-drawer'],
didInsertElement: function () {
let component = this;
let componentJqueryObject = component.$();
let componentElement = componentJqueryObject[0];
let MDCDrawer = mdc.drawer.MDCDrawer;
let drawer = new MDCDrawer(componentElement);
$('header').on('click', '.drawer-menu', function() {
drawer.open = !drawer.open;
});
$('body').on('click', 'main', function() {
drawer.open = false;
});
$('aside').on('click', 'a', function() {
drawer.open = false;
});
..........
For a MDC top-app-bar component (latest MDC only) I've used this
tagName: 'header',
classNames: ['mdc-top-app-bar', 'mdc-top-app-bar--fixed'],
didInsertElement: function () {
let component = this;
let componentJqueryObject = component.$();
let componentElement = componentJqueryObject[0];
let topAppBar = new mdc.topAppBar.MDCTopAppBar(componentElement);
let mainEl = document.getElementById('app-main');
topAppBar.setScrollTarget(mainEl);
............
Note: Its important to always use the didInsertHook for 2 reasons.
1.) According to Ember Docs, didInsert is at that point where the component's HTML is guaranteed to be inserted in the DOM.
2.) According to Fastboot Docs, didInsert is not executed in FastBoot mode which runs in Node yet MDC is a browser thing.
Enjoy!
EDIT: Based on Question below
import Component from '#ember/component';
import { get } from '#ember/object';
import $ from 'jquery';
export default Component.extend({
tagName: 'aside',
classNames: ['mdc-drawer', 'mdc-drawer--modal', 'app-drawer'],
didInsertElement: function () {
let component = this;
let componentJqueryObject = component.$();
let componentElement = componentJqueryObject[0];
let MDCDrawer = mdc.drawer.MDCDrawer;
let drawer = new MDCDrawer(componentElement);
$('header').on('click', '.drawer-menu', function() {
drawer.open = !drawer.open;
});
$('body').on('click', 'main', function() {
drawer.open = false;
});
$('aside').on('click', 'a', function() {
drawer.open = false;
});
// mdc web used to override the a href link element in the drawer component, causing all links to open with a page reload, use this hack if your version still does, assign every a href link a custom-link class and add data attributes to keep things the Ember way
let router = get(component, "router");
component.$().on("click" , ".custom-link" , function(e) {
e.preventDefault();
drawer.open = false;
let routeName = $(this).data("route");
let modelId = $(this).data("id");
if(modelId){
router.transitionTo(routeName , modelId);
} else {
router.transitionTo(routeName);
}
});
}
});

Related

How can I test React Router with Jest?

I have not been able to test React Router with context successfully. I am using
react 0.13.3
react-router 0.13.3
jest 0.3.0
node 0.10.33
and have tried these approaches:
https://labs.chie.do/jest-testing-with-react-router/
https://gist.github.com/alanrubin/da3f740308eb26b20e70
Is there a definitive example?
All links to the "super-secret guide" mentioned in this question (which does not use Jest) are now broken. When I was able to view that guide, it didn't provide any more information than the first link listed above.
Not sure if this is what you're looking for exactly, but I got around this by making a helper function that I use when writing jest tests for components that depend on router state.
//router-test-helper
var Router = require('react-router'),
Route = Router.Route,
TestLocation = require('react-router/lib/locations/TestLocation');
module.exports = function(React){
TestUtils = React.addons.TestUtils;
return {
getRouterComponent: function(targetComponent, mockProps) {
var component,
div = document.createElement('div'),
routes = [
React.createFactory(Route)({
name: '/',
handler: targetComponent
})
];
location = new TestLocation('/');
Router.run(routes, location, function (Handler) {
var mainComponent = React.render(React.createFactory(Handler)(mockProps), div);
component = TestUtils.findRenderedComponentWithType(mainComponent, targetComponent);
});
return component;
}
};
};
I didn't write all of this on my own, most of it I think I pulled from that now defunct guide you linked to. If I remember right... it's been a while.
After you have that you can use this in your tests kinda like this.
//test-example
jest.dontMock('../src/js/someComponent');
var React = require('react/addons');
var TestUtils = React.addons.TestUtils;
var routerHelper = require('../router-test-helper')(React);
var SomeComponent = require('../srcs/js/someComponent');
describe('Some Component', function(){
it('should be testable', function(){
var mockProps = {};
var renderedComponent = routerHelper.getRouterComponent(SomeComponent, mockProps);
// Test your component as usual from here.....
///////////////////////////////////////////////
var inputs = TestUtils.scryRenderedDOMComponentsWithTag(renderedComponent, 'input');
//blah blah blah
});
});
This assumes you have React and the helper in your unmocked module paths
If you're actually trying to test things specific to certain routes, or transitioning between routes... I'm not sure if this is a good approach. May be better to use something more intergration test-y, like selenium or something.. Also... this probably won't work once the 1.0 of react router comes out. But it may be even easier to test things thing 'The React Way'(tm) because all the routing stuff will be handled through props. At least that's the impression I get from the the little bit I've read into it.
To anyone who happens to stuck with this problem.
Here's the setup that I've ended up with for my context dependent components (stripped down for simplicity, of course):
// dontmock.config.js contains jest.dontMock('components/Breadcrumbs')
// to avoid issue with hoisting of import operators, which causes
// jest.dontMock() to be ignored
import dontmock from 'dontmock.config.js';
import React from "react";
import { Router, createMemoryHistory } from "react-router";
import TestUtils from "react-addons-test-utils";
import Breadcrumbs from "components/Breadcrumbs";
// Create history object to operate with in non-browser environment
const history = createMemoryHistory("/products/product/12");
// Setup routes configuration.
// JSX would also work, but this way it's more convenient to specify custom
// route properties (excludes, localized labels, etc..).
const routes = [{
path: "/",
component: React.createClass({
render() { return <div>{this.props.children}</div>; }
}),
childRoutes: [{
path: "products",
component: React.createClass({
render() { return <div>{this.props.children}</div>; }
}),
childRoutes: [{
path: "product/:id",
component: React.createClass({
// Render your component with contextual route props or anything else you need
// If you need to test different combinations of properties, then setup a separate route configuration.
render() { return <Breadcrumbs routes={this.props.routes} />; }
}),
childRoutes: []
}]
}]
}];
describe("Breadcrumbs component test suite:", () => {
beforeEach(function() {
// Render the entire route configuration with Breadcrumbs available on a specified route
this.component = TestUtils.renderIntoDocument(<Router routes={routes} history={history} />);
this.componentNode = ReactDOM.findDOMNode(this.component);
this.breadcrumbNode = ReactDOM.findDOMNode(this.component).querySelector(".breadcrumbs");
});
it("should be defined", function() {
expect(this.breadcrumbNode).toBeDefined();
});
/**
* Now test whatever you need to
*/

How create a unit test for view in Ember CLI that renders the view (like tests for components)

I can't find a single example in google for unit test of views in Ember CLI that renders the view (without renders all app).
I wanna this for test events registered inside of didInserElement hook.
For components i can find docs very easy. For render the component in a test with moduleForComponent just do:
test("component test", function(){
var component = this.subject(),
element = this.append();
ok(element.is('.clickable'), 'has the clickable class');
});
But how i do this for views?
I use this way to render only the view in unit tests:
One important thing to note, is that you need to needs all templates, partials and helpers explicitly. Otherwise the test will fail due to lookup errors.
tests/unit/views/main-test.js:
import Ember from 'ember';
import { test, moduleFor } from 'ember-qunit';
var view;
moduleFor('view:main', 'MainView', {
needs: ['template:main'], // won't find the 'main' template without this
setup: function() {
var controller = Ember.ObjectController.extend({
//mockController if needs
}).create();
view = this.subject({
controller: controller,
templateName: 'main',
});
Ember.run(function() {
view.appendTo('#ember-testing');
});
},
teardown: function() {
Ember.run(function() {
view.destroy();
});
},
});
test("didInsertElement", function(){
var element = Ember.$('.main');
var controller = view.get('controller');
var eventForPressCtrlAltM = Ember.$.Event( "keydown", { which: 77, altKey: true, ctrlKey: true } );
Ember.run(function() {
element.trigger(eventForPressCtrlAltM);
});
strictEqual(/* ... */);
});

Ember.js - Integration Testing with Ember-CLI

I am following a Dockyard Tutorial on using ember-cli with rails. This particular section is on basic integration testing. Unfortunately, it doesn't seem to be registering properly (at least I don't think so). The test should fail and say something along the lines of "Expected: 'Welcome to Boston Ember'"; rather, it says "should pass jshint" which it is and is therefore passing. Any idea what I am doing wrong?
tests/integration/landing-page-test.js
import Ember from 'ember';
import startApp from 'bostonember/tests/helpers/start-app';
var App;
module('Integration - Landing Page', {
setup: function() {
App = startApp();
},
teardown: function() {
Ember.run(App, 'destroy');
}
});
test('Should welcome me to Boston Ember', function() {
visit('/').then(function() {
equal(find('h2#title').text(), 'Welcome bloopde bloopasa to Boston Ember');
});
});
tests/helpers/start-app.js
/* global require */
var Application = require('bostonember/app')['default'];
var Router = require('bostonember/router')['default'];
import Ember from 'ember';
export default function startApp(attrs) {
var App;
var attributes = Ember.merge({
// useful Test defaults
rootElement: '#ember-testing',
LOG_ACTIVE_GENERATION:false,
LOG_VIEW_LOOKUPS: false
}, attrs); // but you can override;
Router.reopen({
location: 'none'
});
Ember.run(function(){
App = Application.create(attributes);
App.setupForTesting();
App.injectTestHelpers();
});
App.reset(); // this shouldn't be needed, i want to be able to "start an app at a specific URL"
return App;
}
Will provide additional info upon request. Thank you!
Must have just been a hiccup?
Solution was to recreate the file, same code, but works now.

Emberjs - Pass an object from the view to controller

I am using a radialProgress as a jQuery plugins (homemade), and I need to implement it for ember but I have some issue to do that.
Quick explanation for the plugins :
var chart = $(yourElement).pieChart(options); // initialise the object to an element
chart.setCompleteProgress( complete, false ); // set how many item you have to complete the task
chart.incrementProgress(); // increment + 1 every time you call it
It's a very simple progress pie.
In my case my task are located inside my controller, but the chart as to select a dom element so I need to initialise it inside my view.
My task in the controller are called from the router from the setupController to reload the model over time.
Here is a small sample of what I would like to do :
App.ApplicationRoute = Ember.Route.extend({
setupController: function(controller) {
var promise = controller.getModel();
this._super(controller, promise);
}
})
App.ApplicationController = Ember.ArrayController.extend({
getModel: function() {
// chart.setcompleteProgress();
// A lot of code are here to get some data
// chart.incrementProgress();
return newModel;
}
})
App.ApplicationView = Ember.View.extend({
didInsertElement: function() {
var chart = $(element).pieChart(opts);
}
})
I don't know how to pass the chart object from the view to the controller to be able to have access to my plugin function.
Che chart won't be inserted into the DOM until the didInsertElement therefore you can't attempt to manipulate it in the route during setupController etc. I'd suggest creating a method in the controller setupChart and calling that on didInsertElement.
App.ApplicationView = Ember.View.extend({
prepPieChart: function() {
var chart = $(element).pieChart(opts);
this.get('controller').setupPieChart(chart);
}.on('didInsertElement')
})
App.ApplicationController = Ember.ArrayController.extend({
setupPieChart: function(chart) {
chart.setcompleteProgress();
// A lot of code are here to get some data
chart.incrementProgress();
}
})
All that being said, maybe it belongs in the view, but I'm not sure of what you're completely doing.

EmberJS: Loading a child URL directly in a master-detail app with an SSE-backed model on the master controller

I'm trying to develop this single-module application with a master-detail relationship between two routes. The master is supposed to have a model which is initially loaded and subsequently updated with a single Server-Side-Events entry point. I have the following code so far:
var App = Ember.Application.create({
rootElement: '#content'
});
App.Router.map(function() {
this.resource('receptores', {path: '/'}, function() {
this.resource('receptor', {path: ':user_id'});
});
});
App.ReceptoresController = Ember.Controller.extend({
init: function() {
var sse = new EventSource('/push');
var controller = this;
sse.addEventListener('update-hhs', function(e) {
controller.set('model', JSON.parse(e.data).receptores);
});
}
});
App.ReceptorRoute = Ember.Route.extend({
model: function(params) {
return this.modelFor('receptores').findBy('id', params.user_id);
}
});
Loading the 'receptores' works and currently the data loads fine. Clicking on a link-to generated link as http://localhost:5003/#/skkkd, for example, will load the 'receptor' route and the detail data as well (the detail is just a subset of the same data loaded on the master).
If I try reloading at a detail url or entering http://localhost:5003/#/skkkd directly I'll get the following exception:
Error while loading route: TypeError: Cannot read property 'findBy' of undefined
at App.ReceptorRoute.Ember.Route.extend.model (http://localhost:5003/monitoreo/static/js/receptores.js:34:43)
at superWrapper [as model] (http://localhost:5003/static/js/ember-1.5.1.js:1292:16)
at Ember.Route.Ember.Object.extend.deserialize (http://localhost:5003/static/js/ember-1.5.1.js:36570:19)
at http://localhost:5003/static/js/ember-1.5.1.js:32972:57
at http://localhost:5003/static/js/ember-1.5.1.js:33464:19
at invokeResolver (http://localhost:5003/static/js/ember-1.5.1.js:9646:9)
at new Promise (http://localhost:5003/static/js/ember-1.5.1.js:9632:9)
at Router.async (http://localhost:5003/static/js/ember-1.5.1.js:33463:16)
at Object.HandlerInfo.runSharedModelHook (http://localhost:5003/static/js/ember-1.5.1.js:32971:16)
at Object.UnresolvedHandlerInfoByParam.getModel (http://localhost:5003/static/js/ember-1.5.1.js:33058:19)
I know the problem is the init hook isn't being called for ReceptoresController. I think what I should do is implement a model hook on either a ReceptoresRoute or the controller and load initial data through a jQuery.get() and have the server-side-events only update. But then where do I initialize the SSE?
Edit:
It turns out I misunderstood how the model hook works on child routes. In my case, I normally view the detail page through a link-to link, so Ember already provides the model for receptor. The model hook is never called in that case. It is only called when trying to access the detail page directly which is where it fails. So my problem remains.
My whole problem boils down to where I should place the SSE initialization so that both routes can have access to the same model regardless of which was loaded.
Alright, this is my solution. Pieced together from a couple questions here. Namely https://stackoverflow.com/a/21988143/410224 and https://stackoverflow.com/a/21752808/410224. Hope it helps someone out.
var App = Ember.Application.create({
rootElement: '#content'
});
App.Router.map(function() {
this.resource('receptores', {path: '/'}, function() {
this.resource('receptor', {path: ':user_id'});
});
});
App.ReceptoresRoute = Ember.Route.extend({
model: function() {
var deferredData = Ember.Deferred.create();
var data = [];
var sse = new EventSource('/push');
sse.addEventListener('update-hhs', function(e) {
var receptores = JSON.parse(e.data).receptores;
receptores.forEach(function(r) {
var item = data.findBy('id', r.id);
if (typeof(item) != 'undefined') {
data.replace(data.indexOf(item), 1, [r]);
} else {
data.pushObject(r);
data.sortBy('full_name');
}
});
deferredData.resolve(data);
});
return deferredData;
}
});
App.ReceptorRoute = Ember.Route.extend({
model: function(params) {
return this.modelFor('receptores').findBy('id', params.user_id);
}
});