├── app ├── .gitkeep └── helpers │ ├── dispatch.js │ └── get-state.js ├── addon ├── .gitkeep ├── helpers │ ├── get-state.js │ └── dispatch.js └── _private │ └── closure-action.js ├── vendor └── .gitkeep ├── tests ├── unit │ └── .gitkeep ├── integration │ ├── .gitkeep │ └── components │ │ └── helper-test.js ├── dummy │ ├── app │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── models │ │ │ └── .gitkeep │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── components │ │ │ ├── .gitkeep │ │ │ ├── x-child.js │ │ │ ├── x-parent.js │ │ │ └── heart-progress.js │ │ ├── controllers │ │ │ └── .gitkeep │ │ ├── templates │ │ │ ├── components │ │ │ │ └── .gitkeep │ │ │ └── application.hbs │ │ ├── resolver.js │ │ ├── enhancers │ │ │ └── index.js │ │ ├── reducers │ │ │ ├── low.js │ │ │ ├── high.js │ │ │ ├── name.js │ │ │ ├── index.js │ │ │ └── progress.js │ │ ├── router.js │ │ ├── app.js │ │ ├── index.html │ │ └── styles │ │ │ └── app.css │ ├── public │ │ ├── robots.txt │ │ ├── ember.png │ │ ├── redux.png │ │ └── crossdomain.xml │ └── config │ │ └── environment.js ├── test-helper.js ├── helpers │ ├── destroy-app.js │ ├── resolver.js │ ├── start-app.js │ └── module-for-acceptance.js ├── .jshintrc └── index.html ├── .watchmanconfig ├── .bowerrc ├── blueprints ├── .jshintrc └── ember-redux-helpers │ └── index.js ├── index.js ├── config ├── environment.js └── ember-try.js ├── .npmignore ├── testem.js ├── bower.json ├── .ember-cli ├── .gitignore ├── .jshintrc ├── .editorconfig ├── .travis.yml ├── ember-cli-build.js ├── LICENSE.md ├── package.json └── README.md /app/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /addon/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/integration/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components", 3 | "analytics": false 4 | } 5 | -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /app/helpers/dispatch.js: -------------------------------------------------------------------------------- 1 | export { default, dispatch } from 'ember-redux-helpers/helpers/dispatch'; 2 | -------------------------------------------------------------------------------- /app/helpers/get-state.js: -------------------------------------------------------------------------------- 1 | export { default, getState } from 'ember-redux-helpers/helpers/get-state'; 2 | -------------------------------------------------------------------------------- /blueprints/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "console" 4 | ], 5 | "strict": false 6 | } 7 | -------------------------------------------------------------------------------- /tests/dummy/app/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from 'ember-resolver'; 2 | 3 | export default Resolver; 4 | -------------------------------------------------------------------------------- /tests/dummy/app/enhancers/index.js: -------------------------------------------------------------------------------- 1 | export default window.devToolsExtension ? window.devToolsExtension() : f => f; 2 | -------------------------------------------------------------------------------- /tests/dummy/public/ember.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jkusa/ember-redux-helpers/HEAD/tests/dummy/public/ember.png -------------------------------------------------------------------------------- /tests/dummy/public/redux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jkusa/ember-redux-helpers/HEAD/tests/dummy/public/redux.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 'use strict'; 3 | 4 | module.exports = { 5 | name: 'ember-redux-helpers' 6 | }; 7 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | 'use strict'; 3 | 4 | module.exports = function(/* environment, appConfig */) { 5 | return { }; 6 | }; 7 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import resolver from './helpers/resolver'; 2 | import { 3 | setResolver 4 | } from 'ember-qunit'; 5 | 6 | setResolver(resolver); 7 | -------------------------------------------------------------------------------- /tests/helpers/destroy-app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default function destroyApp(application) { 4 | Ember.run(application, 'destroy'); 5 | } 6 | -------------------------------------------------------------------------------- /tests/dummy/app/reducers/low.js: -------------------------------------------------------------------------------- 1 | export default ((state=0, action) => { // jshint ignore:line 2 | if(action.type === 'UP') { 3 | return state + 1; 4 | } 5 | return state; 6 | }); 7 | -------------------------------------------------------------------------------- /tests/dummy/app/reducers/high.js: -------------------------------------------------------------------------------- 1 | export default ((state=9, action) => { // jshint ignore:line 2 | if(action.type === 'DOWN') { 3 | return state - 1; 4 | } 5 | return state; 6 | }); 7 | -------------------------------------------------------------------------------- /tests/dummy/app/reducers/name.js: -------------------------------------------------------------------------------- 1 | export default ((state='Tomster', action) => { // jshint ignore:line 2 | if(action.type === 'UPDATE_NAME') { 3 | return action.name; 4 | } 5 | return state; 6 | }); 7 | -------------------------------------------------------------------------------- /tests/dummy/app/reducers/index.js: -------------------------------------------------------------------------------- 1 | import low from 'dummy/reducers/low'; 2 | import high from 'dummy/reducers/high'; 3 | import name from 'dummy/reducers/name'; 4 | import progress from 'dummy/reducers/progress'; 5 | 6 | export default { low, high, name, progress }; 7 | -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import config from './config/environment'; 3 | 4 | const Router = Ember.Router.extend({ 5 | location: config.locationType 6 | }); 7 | 8 | Router.map(function() { 9 | }); 10 | 11 | export default Router; 12 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /bower_components 2 | /config/ember-try.js 3 | /dist 4 | /tests 5 | /tmp 6 | **/.gitkeep 7 | .bowerrc 8 | .editorconfig 9 | .ember-cli 10 | .gitignore 11 | .jshintrc 12 | .watchmanconfig 13 | .travis.yml 14 | bower.json 15 | ember-cli-build.js 16 | testem.js 17 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | module.exports = { 3 | "framework": "qunit", 4 | "test_page": "tests/index.html?hidepassed", 5 | "disable_watching": true, 6 | "launch_in_ci": [ 7 | "PhantomJS" 8 | ], 9 | "launch_in_dev": [ 10 | "PhantomJS", 11 | "Chrome" 12 | ] 13 | }; 14 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-redux-helpers", 3 | "dependencies": { 4 | "ember": "~2.5.0", 5 | "ember-cli-shims": "0.1.1", 6 | "ember-cli-test-loader": "0.2.2", 7 | "ember-qunit-notifications": "0.1.0", 8 | "font-awesome": "~4.5.0", 9 | "highlightjs": "^9.4.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/dummy/app/components/x-child.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import hbs from 'htmlbars-inline-precompile'; 3 | 4 | export default Ember.Component.extend({ 5 | layout: hbs` 6 | {{high}} 7 | 8 | ` 9 | }); 10 | -------------------------------------------------------------------------------- /.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": false 9 | } 10 | -------------------------------------------------------------------------------- /tests/helpers/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from '../../resolver'; 2 | import config from '../../config/environment'; 3 | 4 | const resolver = Resolver.create(); 5 | 6 | resolver.namespace = { 7 | modulePrefix: config.modulePrefix, 8 | podModulePrefix: config.podModulePrefix 9 | }; 10 | 11 | export default resolver; 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | 7 | # dependencies 8 | /node_modules 9 | /bower_components 10 | 11 | # misc 12 | /.sass-cache 13 | /connect.lock 14 | /coverage/* 15 | /libpeerconnection.log 16 | npm-debug.log 17 | testem.log 18 | -------------------------------------------------------------------------------- /tests/dummy/app/reducers/progress.js: -------------------------------------------------------------------------------- 1 | export default ((state=80, action) => { // jshint ignore:line 2 | if(action.type === 'MORE') { 3 | if(state < 100) { 4 | state = state + 10; 5 | } 6 | } 7 | 8 | if(action.type === 'LESS') { 9 | if(state > 0) { 10 | state = state - 10; 11 | } 12 | } 13 | 14 | return state; 15 | }); 16 | -------------------------------------------------------------------------------- /addon/helpers/get-state.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Helper.extend({ 4 | 5 | redux: Ember.inject.service('redux'), 6 | 7 | init() { 8 | this._super(...arguments); 9 | this.get('redux').subscribe(() => { 10 | this.recompute(); 11 | }); 12 | }, 13 | 14 | compute(params) { 15 | let state = this.get('redux').getState(); 16 | return Ember.get(state, params[0]); 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /addon/_private/closure-action.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | let ClosureActionModule; 4 | 5 | if ('ember-htmlbars/keywords/closure-action' in Ember.__loader.registry) { 6 | ClosureActionModule = Ember.__loader.require('ember-htmlbars/keywords/closure-action'); 7 | } else { 8 | ClosureActionModule = Ember.__loader.require('ember-routing-htmlbars/keywords/closure-action'); 9 | } 10 | 11 | export default ClosureActionModule.ACTION; 12 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Resolver from './resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from './config/environment'; 5 | 6 | let App; 7 | 8 | Ember.MODEL_FACTORY_INJECTIONS = true; 9 | 10 | App = Ember.Application.extend({ 11 | modulePrefix: config.modulePrefix, 12 | podModulePrefix: config.podModulePrefix, 13 | Resolver 14 | }); 15 | 16 | loadInitializers(App, config.modulePrefix); 17 | 18 | export default App; 19 | -------------------------------------------------------------------------------- /blueprints/ember-redux-helpers/index.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | module.exports = { 3 | 4 | description: 'Set up additional packages for Ember apps', 5 | 6 | normalizeEntityName: function() {}, 7 | 8 | afterInstall: function(options) { 9 | return this.addPackagesToProject([ 10 | { name: 'ember-browserify', target: '^1.1.9' }, 11 | { name: 'ember-redux', target: '^1.4.0' }, 12 | { name: 'redux', target: '^3.4.0' }, 13 | { name: 'redux-thunk', target: '^2.0.1' } 14 | ]); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /tests/helpers/start-app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Application from '../../app'; 3 | import config from '../../config/environment'; 4 | 5 | export default function startApp(attrs) { 6 | let application; 7 | 8 | let attributes = Ember.merge({}, config.APP); 9 | attributes = Ember.merge(attributes, attrs); // use defaults, but you can override; 10 | 11 | Ember.run(() => { 12 | application = Application.create(attributes); 13 | application.setupForTesting(); 14 | application.injectTestHelpers(); 15 | }); 16 | 17 | return application; 18 | } 19 | -------------------------------------------------------------------------------- /tests/dummy/app/components/x-parent.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import hbs from 'htmlbars-inline-precompile'; 3 | 4 | export default Ember.Component.extend({ 5 | layout: hbs` 6 | {{get-state "low"}} 7 | 8 | {{x-child 9 | high=(get-state "high") 10 | down=(dispatch "DOWN") 11 | }} 12 | {{get-state "name"}} 13 | 14 | ` 15 | }); 16 | -------------------------------------------------------------------------------- /addon/helpers/dispatch.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import ACTION from '../_private/closure-action'; 3 | 4 | export default Ember.Helper.extend({ 5 | 6 | redux: Ember.inject.service('redux'), 7 | 8 | compute([type, ...params], hash) { 9 | let redux = this.get('redux'); 10 | 11 | let action = (...invocationArgs) => { 12 | Ember.run.join(() => { 13 | redux.dispatch( 14 | Object.assign({}, hash, { 15 | type, 16 | invocationArgs 17 | }) 18 | ); 19 | }); 20 | }; 21 | 22 | action[ACTION] = true; 23 | return action; 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /tests/helpers/module-for-acceptance.js: -------------------------------------------------------------------------------- 1 | import { module } from 'qunit'; 2 | import startApp from '../helpers/start-app'; 3 | import destroyApp from '../helpers/destroy-app'; 4 | 5 | export default function(name, options = {}) { 6 | module(name, { 7 | beforeEach() { 8 | this.application = startApp(); 9 | 10 | if (options.beforeEach) { 11 | options.beforeEach.apply(this, arguments); 12 | } 13 | }, 14 | 15 | afterEach() { 16 | if (options.afterEach) { 17 | options.afterEach.apply(this, arguments); 18 | } 19 | 20 | destroyApp(this.application); 21 | } 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "document", 4 | "window", 5 | "-Promise" 6 | ], 7 | "browser": true, 8 | "boss": true, 9 | "curly": true, 10 | "debug": false, 11 | "devel": true, 12 | "eqeqeq": true, 13 | "evil": true, 14 | "forin": false, 15 | "immed": false, 16 | "laxbreak": false, 17 | "newcap": true, 18 | "noarg": true, 19 | "noempty": false, 20 | "nonew": false, 21 | "nomen": false, 22 | "onevar": false, 23 | "plusplus": false, 24 | "regexp": false, 25 | "undef": true, 26 | "sub": true, 27 | "strict": false, 28 | "white": false, 29 | "eqnull": true, 30 | "esnext": true, 31 | "unused": true 32 | } 33 | -------------------------------------------------------------------------------- /tests/dummy/public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.js] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | [*.hbs] 21 | insert_final_newline = false 22 | indent_style = space 23 | indent_size = 2 24 | 25 | [*.css] 26 | indent_style = space 27 | indent_size = 2 28 | 29 | [*.html] 30 | indent_style = space 31 | indent_size = 2 32 | 33 | [*.{diff,md}] 34 | trim_trailing_whitespace = false 35 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | node_js: 4 | - "0.12" 5 | 6 | sudo: false 7 | 8 | cache: 9 | directories: 10 | - node_modules 11 | 12 | env: 13 | - EMBER_TRY_SCENARIO=default 14 | - EMBER_TRY_SCENARIO=ember-1.13 15 | - EMBER_TRY_SCENARIO=ember-release 16 | - EMBER_TRY_SCENARIO=ember-beta 17 | - EMBER_TRY_SCENARIO=ember-canary 18 | 19 | matrix: 20 | fast_finish: true 21 | allow_failures: 22 | - env: EMBER_TRY_SCENARIO=ember-canary 23 | 24 | before_install: 25 | - export PATH=/usr/local/phantomjs-2.0.0/bin:$PATH 26 | - "npm config set spin false" 27 | - "npm install -g npm@^2" 28 | 29 | install: 30 | - npm install -g bower 31 | - npm install 32 | - bower install 33 | 34 | script: 35 | - ember try $EMBER_TRY_SCENARIO test 36 | -------------------------------------------------------------------------------- /ember-cli-build.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | /* global require, module */ 3 | var EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 4 | 5 | module.exports = function(defaults) { 6 | var app = new EmberAddon(defaults, { 7 | babel: { 8 | includePolyfill: true 9 | } 10 | }); 11 | 12 | /* 13 | This build file specifies the options for the dummy test app of this 14 | addon, located in `/tests/dummy` 15 | This build file does *not* influence how the addon or the app using it 16 | behave. You most likely want to be modifying `./index.js` or app's build file 17 | */ 18 | app.import('bower_components/highlightjs/styles/github.css'); 19 | app.import('bower_components/highlightjs/highlight.pack.js'); 20 | return app.toTree(); 21 | }; 22 | -------------------------------------------------------------------------------- /tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | 12 | 13 | 14 | 15 | {{content-for "head-footer"}} 16 | 17 | 18 | {{content-for "body"}} 19 | 20 | 21 | 22 | 23 | 24 | {{content-for "body-footer"}} 25 | 26 | 27 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /tests/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "document", 4 | "window", 5 | "location", 6 | "setTimeout", 7 | "$", 8 | "-Promise", 9 | "define", 10 | "console", 11 | "visit", 12 | "exists", 13 | "fillIn", 14 | "click", 15 | "keyEvent", 16 | "triggerEvent", 17 | "find", 18 | "findWithAssert", 19 | "wait", 20 | "DS", 21 | "andThen", 22 | "currentURL", 23 | "currentPath", 24 | "currentRouteName", 25 | "ProgressBar" 26 | ], 27 | "node": false, 28 | "browser": false, 29 | "boss": true, 30 | "curly": true, 31 | "debug": false, 32 | "devel": false, 33 | "eqeqeq": true, 34 | "evil": true, 35 | "forin": false, 36 | "immed": false, 37 | "laxbreak": false, 38 | "newcap": true, 39 | "noarg": true, 40 | "noempty": false, 41 | "nonew": false, 42 | "nomen": false, 43 | "onevar": false, 44 | "plusplus": false, 45 | "regexp": false, 46 | "undef": true, 47 | "sub": true, 48 | "strict": false, 49 | "white": false, 50 | "eqnull": true, 51 | "esnext": true, 52 | "unused": true 53 | } 54 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy Tests 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | {{content-for "test-head"}} 12 | 13 | 14 | 15 | 16 | 17 | {{content-for "head-footer"}} 18 | {{content-for "test-head-footer"}} 19 | 20 | 21 | {{content-for "body"}} 22 | {{content-for "test-body"}} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | {{content-for "body-footer"}} 32 | {{content-for "test-body-footer"}} 33 | 34 | 35 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | module.exports = { 3 | scenarios: [ 4 | { 5 | name: 'default', 6 | bower: { 7 | dependencies: { } 8 | } 9 | }, 10 | { 11 | name: 'ember-1.13', 12 | bower: { 13 | dependencies: { 14 | 'ember': '~1.13.0' 15 | }, 16 | resolutions: { 17 | 'ember': '~1.13.0' 18 | } 19 | } 20 | }, 21 | { 22 | name: 'ember-release', 23 | bower: { 24 | dependencies: { 25 | 'ember': 'components/ember#release' 26 | }, 27 | resolutions: { 28 | 'ember': 'release' 29 | } 30 | } 31 | }, 32 | { 33 | name: 'ember-beta', 34 | bower: { 35 | dependencies: { 36 | 'ember': 'components/ember#beta' 37 | }, 38 | resolutions: { 39 | 'ember': 'beta' 40 | } 41 | } 42 | }, 43 | { 44 | name: 'ember-canary', 45 | bower: { 46 | dependencies: { 47 | 'ember': 'components/ember#canary' 48 | }, 49 | resolutions: { 50 | 'ember': 'canary' 51 | } 52 | } 53 | } 54 | ] 55 | }; 56 | -------------------------------------------------------------------------------- /tests/dummy/config/environment.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 3 | module.exports = function(environment) { 4 | var ENV = { 5 | modulePrefix: 'dummy', 6 | environment: environment, 7 | baseURL: '/', 8 | locationType: 'auto', 9 | EmberENV: { 10 | FEATURES: { 11 | // Here you can enable experimental features on an ember canary build 12 | // e.g. 'with-controller': true 13 | } 14 | }, 15 | 16 | APP: { 17 | // Here you can pass flags/options to your application instance 18 | // when it is created 19 | } 20 | }; 21 | 22 | if (environment === 'development') { 23 | // ENV.APP.LOG_RESOLVER = true; 24 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 25 | // ENV.APP.LOG_TRANSITIONS = true; 26 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 27 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 28 | } 29 | 30 | if (environment === 'test') { 31 | // Testem prefers this... 32 | ENV.baseURL = '/'; 33 | ENV.locationType = 'none'; 34 | 35 | // keep test console output quieter 36 | ENV.APP.LOG_ACTIVE_GENERATION = false; 37 | ENV.APP.LOG_VIEW_LOOKUPS = false; 38 | 39 | ENV.APP.rootElement = '#ember-testing'; 40 | } 41 | 42 | if (environment === 'production') { 43 | ENV.locationType = 'hash'; 44 | ENV.baseURL = '/ember-redux-helpers/'; 45 | 46 | } 47 | 48 | return ENV; 49 | }; 50 | -------------------------------------------------------------------------------- /tests/dummy/app/components/heart-progress.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import hbs from 'htmlbars-inline-precompile'; 3 | 4 | const { computed, get, set } = Ember; 5 | 6 | export default Ember.Component.extend({ 7 | layout: hbs` 8 |
9 | {{yield}} 10 |
11 |
12 | 13 | 14 | 15 | 16 |
17 | `, 18 | 19 | classNames: ['heart-progress'], 20 | 21 | didInsertElement() { 22 | this._super(...arguments); 23 | let heart = new ProgressBar.Path(`#${this.elementId} #heart-path`); 24 | set(this, 'heart', heart); 25 | 26 | heart.set(get(this, 'value')); 27 | }, 28 | 29 | didUpdateAttrs() { 30 | let heart = get(this, 'heart'); 31 | if(heart) { 32 | heart.set(get(this, 'value')); 33 | } 34 | }, 35 | 36 | value: computed('progress', function() { 37 | let progress = get(this, 'progress'); 38 | if(progress) { 39 | return progress/100; 40 | } 41 | return 0; 42 | }) 43 | }); 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-redux-helpers", 3 | "version": "0.1.1", 4 | "description": "A set of Ember Template Helpers for Redux.JS", 5 | "directories": { 6 | "doc": "doc", 7 | "test": "tests" 8 | }, 9 | "scripts": { 10 | "build": "ember build", 11 | "start": "ember server", 12 | "test": "ember try:testall" 13 | }, 14 | "repository": "https://github.com/jkusa/ember-redux-helpers.git", 15 | "engines": { 16 | "node": ">= 0.10.0" 17 | }, 18 | "author": "Jon Kilroy ", 19 | "license": "MIT", 20 | "devDependencies": { 21 | "broccoli-asset-rev": "^2.4.2", 22 | "ember-ajax": "0.7.1", 23 | "ember-browserify": "^1.1.9", 24 | "ember-cli": "2.5.1", 25 | "ember-cli-app-version": "^1.0.0", 26 | "ember-cli-dependency-checker": "^1.2.0", 27 | "ember-cli-font-awesome": "1.5.0", 28 | "ember-cli-github-pages": "0.1.0", 29 | "ember-cli-htmlbars": "^1.0.3", 30 | "ember-cli-htmlbars-inline-precompile": "^0.3.1", 31 | "ember-cli-inject-live-reload": "^1.4.0", 32 | "ember-cli-jshint": "^1.0.0", 33 | "ember-cli-qunit": "^1.4.0", 34 | "ember-cli-release": "0.2.8", 35 | "ember-cli-sri": "^2.1.0", 36 | "ember-cli-uglify": "^1.2.0", 37 | "ember-data": "^2.5.0", 38 | "ember-disable-prototype-extensions": "^1.1.0", 39 | "ember-export-application-global": "^1.0.5", 40 | "ember-load-initializers": "^0.5.1", 41 | "ember-progress-bar": "0.0.3", 42 | "ember-redux": "^1.4.0", 43 | "ember-resolver": "^2.0.3", 44 | "ember-try": "^0.2.2", 45 | "loader.js": "^4.0.1", 46 | "progressbar.js": "^1.0.1", 47 | "redux": "^3.4.0", 48 | "redux-thunk": "^2.0.1" 49 | }, 50 | "keywords": [ 51 | "ember-addon", 52 | "ember-cli", 53 | "ember", 54 | "redux" 55 | ], 56 | "dependencies": { 57 | "ember-cli-babel": "^5.1.6" 58 | }, 59 | "ember-addon": { 60 | "configPath": "tests/dummy/config", 61 | "demoURL": "http://jkusa.github.io/ember-redux-helpers" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ember-redux-helpers 2 | 3 | [![Build Status](https://travis-ci.org/jkusa/ember-redux-helpers.svg?branch=master)](https://travis-ci.org/jkusa/ember-redux-helpers) 4 | 5 | A set of [Ember](http://emberjs.com) Template Helpers for [Redux.JS](http://redux.js.org) 6 | 7 | ## Demo Page 8 | 9 | http://jkusa.github.io/ember-redux-helpers/ 10 | 11 | ## Usage 12 | 13 | ### `{{get-state "state.path"}}` 14 | 15 | Helper to fetch and subscribe to state properties in the redux store 16 | 17 | ```hbs 18 | {!-- component.hbs --}} 19 | {{progress-bar 20 | progress=(get-state 'progress') 21 | }} 22 | ``` 23 | 24 | Use object paths just like you would with [Ember.get](http://emberjs.com/api/#method_get) 25 | 26 | ```hbs 27 | {{!-- component.hbs --}} 28 | {{todo-item 29 | todo=(get-state 'todos.firstObject') 30 | }} 31 | ``` 32 | 33 | ### `{{dispatch "TYPE" key=value key=value}}` 34 | 35 | Closure action helper to dispatch directly to the redux store 36 | 37 | ```hs 38 | {{!-- component.hbs --}} 39 | 42 | ``` 43 | 44 | ```js 45 | //reducer.js 46 | export default (state=0, action) => { 47 | 48 | if(action.type === 'ADD') { 49 | state += action.value; 50 | } 51 | 52 | return state; 53 | }; 54 | ``` 55 | 56 | Arguments provided while invoking the action can be referenced via the __invocationArgs__ property array 57 | 58 | ```hbs 59 | {{!-- component.hbs --}} 60 | 61 | ``` 62 | 63 | ```js 64 | //reducer.js 65 | export default (state={}, action) => { 66 | 67 | if (action.type === 'UPDATE') { 68 | 69 | let { field, invocationArgs } = action; 70 | state = Object.assign(state, { 71 | //invocaionArgs contains the event obj 72 | [field]: invocationArgs[0].target.value 73 | }); 74 | 75 | } 76 | 77 | return state; 78 | }; 79 | ``` 80 | 81 | ## Compatibility 82 | 83 | This addon will work on Ember versions `1.13.x` and up only, due to use of the new `Helper` implementation. 84 | 85 | ## Thanks 86 | 87 | Thanks to @toranb and @rwjblue who inspired this addon. 88 | -------------------------------------------------------------------------------- /tests/integration/components/helper-test.js: -------------------------------------------------------------------------------- 1 | import { moduleForComponent, test } from 'ember-qunit'; 2 | import hbs from 'htmlbars-inline-precompile'; 3 | 4 | moduleForComponent('x-parent', 'integration: redux helpers test', { 5 | integration: true, 6 | setup() { 7 | this.inject.service('redux'); 8 | } 9 | }); 10 | 11 | test('dispatch and get-state can be used to interact with the redux store', function(assert) { 12 | assert.expect(8); 13 | 14 | this.render(hbs`{{x-parent}}`); 15 | 16 | let $parent = this.$('.low-state'), 17 | $child = this.$('.high-state'), 18 | $name = this.$('.name-state'); 19 | 20 | /* == Test initial state == */ 21 | 22 | assert.equal($parent.text(), 23 | 0, 24 | 'parent rendered the correct initial low state from redux'); 25 | 26 | assert.equal($child.text(), 27 | 9, 28 | 'child received the correct initial high state from redux as a prop'); 29 | 30 | assert.equal($name.text(), 31 | 'Tomster', 32 | 'parent rendered the correct initial name state from redux'); 33 | 34 | /* == Test Dispatch and Rerender == */ 35 | 36 | this.$('.btn-up').trigger('click'); 37 | assert.equal($parent.text(), 38 | 1, 39 | 'dispatch action was correctly called and parent rerendered'); 40 | 41 | this.$('.btn-down').trigger('click'); 42 | assert.equal($child.text(), 43 | 8, 44 | 'dispatch closure action was correctly called and childed rerendered'); 45 | 46 | /* == Test Dispatch and Rerender Again == */ 47 | 48 | this.$('.btn-up').trigger('click'); 49 | assert.equal($parent.text(), 50 | 2, 51 | 'dispatch action was correctly called and parent rerendered'); 52 | 53 | this.$('.btn-down').trigger('click'); 54 | assert.equal($child.text(), 55 | 7, 56 | 'dispatch closure action was correctly called and childed rerendered'); 57 | 58 | /* == Test Dispatch with Parameter == */ 59 | 60 | this.$('.btn-name').trigger('click'); 61 | assert.equal($name.text(), 62 | 'Zoey', 63 | 'dispatch action with parameter was correctly called and parent rerendered'); 64 | }); 65 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 |
2 | 3 | {{#heart-progress progress=(get-state 'progress')}} 4 |
5 | ember logo 6 |
+
7 | redux logo 8 |
9 |
10 |
\{{get-state 'progress'}}:
{{get-state 'progress'}} 11 |
12 | {{/heart-progress}} 13 | 14 |
15 | 18 | 21 |
22 | 23 |

Ember Redux Helpers

24 |

A set of Ember Template Helpers for redux.js

25 | ember install ember-redux-helpers 26 | 27 | 32 |
33 | 34 |
35 |
36 |

\{{get-state "state.path"}}

37 |
38 | helper to fetch and subscribe to state properties in the redux store 39 |
40 |
41 |

 42 | \{{!-- component.hbs --}}
 43 | \{{progress-bar
 44 |   progress=(get-state 'progress')
 45 | }}
 46 |       
47 |
48 |
49 | Use object paths just like you would with Ember.get 50 |
51 |
52 |

 53 | \{{!-- component.hbs --}}
 54 | \{{todo-item
 55 |   todo=(get-state 'todos.firstObject')
 56 | }}
 57 |       
58 |
59 |
60 | 61 |
62 |

\{{dispatch "TYPE" prop=value prop=value}}

63 |
64 | closure action helper to dispatch directly to the redux store 65 |
66 |
67 |

 68 | \{{!-- component.hbs --}}
 69 | <button
 70 |   onclick=\{{dispatch 'ADD' value=value}}>
 71 |   Click to Add
 72 | </button>
 73 |       
74 |

 75 | //reducer.js
 76 | export default (state=0, action) => {
 77 | 
 78 |   if(action.type === 'ADD') {
 79 |     state += action.value;
 80 |   }
 81 | 
 82 |   return state;
 83 | };
 84 |       
85 |
86 |
87 | arguments provided while invoking the action can be referenced via the invocationArgs array property 88 |
89 |
90 |

 91 | \{{!-- component.hbs --}}
 92 | <input
 93 |  onchange=(dispatch 'UPDATE' field='title')
 94 | >
 95 |       
96 |

 97 | //reducer.js
 98 | export default (state={}, action) => {
 99 | 
100 |   if (action.type === 'UPDATE') {
101 | 
102 |     let { field, invocationArgs } = action;
103 |     state = Object.assign(state, {
104 |       //invocaionArgs contains the event obj
105 |       [field]: invocationArgs[0].target.value
106 |     });
107 | 
108 |   }
109 | 
110 |   return state;
111 | };
112 |       
113 |
114 |
115 |
116 | 117 | 124 | 125 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0 auto; 3 | padding: 15px; 4 | width: 400px; 5 | font-family: HelveticaNeue-Light,"Helvetica Neue Light","Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif; 6 | font-weight: 300; 7 | color: #4B4B4D; 8 | background-color: #FFFDFD; 9 | } 10 | 11 | a { 12 | text-decoration: inherit; 13 | color: inherit; 14 | cursor: pointer; 15 | border: dotted #555; 16 | border-width: 0 0 1px 0; 17 | } 18 | 19 | button { 20 | cursor: pointer; 21 | } 22 | 23 | button:focus { 24 | outline: 0; 25 | } 26 | 27 | h1 { 28 | margin: 5px 0 0; 29 | } 30 | 31 | h2 { 32 | font-size: 18px; 33 | text-align: center; 34 | font-weight: 100; 35 | margin-top: 0; 36 | } 37 | 38 | svg { 39 | display: block; 40 | margin: auto; 41 | height: 100%; 42 | } 43 | 44 | .progress, 45 | code { 46 | font-size: 12px; 47 | font-family: Menlo,Monaco,Consolas,"Courier New",monospace; 48 | } 49 | 50 | header { 51 | width: 100%; 52 | text-align: center; 53 | } 54 | 55 | pre { 56 | margin: 0; 57 | } 58 | 59 | section { 60 | margin: 20px; 61 | border: 1px solid #CCC; 62 | border-radius: 5px; 63 | } 64 | 65 | footer { 66 | margin: 20px 67 | } 68 | 69 | em { 70 | color: #0086b3; 71 | } 72 | 73 | section h3 { 74 | border-bottom: 1px solid #CCC; 75 | margin: 0; 76 | padding: 10px; 77 | font-size: 16px; 78 | text-align: center; 79 | } 80 | 81 | .description { 82 | padding: 10px; 83 | border-bottom: 1px solid #CCC; 84 | border-top: 1px solid #CCC; 85 | text-align: center; 86 | } 87 | 88 | .example { 89 | padding: 10px; 90 | } 91 | 92 | .example code { 93 | font-size: 12px; 94 | } 95 | 96 | .install-cmd { 97 | margin: 20px 0; 98 | display: block; 99 | } 100 | 101 | .progress-btn-group { 102 | justify-content: space-around; 103 | display: flex; 104 | } 105 | 106 | .btn code { 107 | margin: 3px; 108 | font-size: 10px; 109 | } 110 | 111 | .plus { 112 | font-size: 30px; 113 | } 114 | 115 | .btn.dispatch { 116 | z-index: 1; 117 | margin: 9px 0; 118 | border-radius: 3px; 119 | border: 1px solid #E34C32; 120 | background: transparent; 121 | color: #E34C32; 122 | font-weight: 600; 123 | } 124 | 125 | .btn.dispatch .hljs { 126 | background-color: transparent; 127 | } 128 | 129 | .btn.dispatch:hover { 130 | background-color: rgba(0,0,0,0.01); 131 | border-color: #4B4B4D; 132 | } 133 | 134 | .heart-progress { 135 | height: 350px; 136 | position: relative; 137 | } 138 | 139 | .inner-container { 140 | top: -15px; 141 | width: 100%; 142 | height: 100%; 143 | flex-direction: column; 144 | margin: auto auto; 145 | position: absolute; 146 | display: flex; 147 | justify-content: center; 148 | align-items: center; 149 | } 150 | 151 | .logos { 152 | display: flex; 153 | align-items: center; 154 | margin: 10px; 155 | } 156 | 157 | .redux { 158 | padding: 0 8px; 159 | } 160 | 161 | .progress { 162 | display: flex; 163 | align-items: center; 164 | } 165 | 166 | .progress .hljs { 167 | background-color: transparent; 168 | } 169 | 170 | .profile-link { 171 | text-align: center; 172 | } 173 | 174 | a.ember-link, 175 | .profile-link a { 176 | border: none; 177 | } 178 | 179 | .gh-link { 180 | text-align: center; 181 | margin: 20px 0; 182 | } 183 | 184 | .gh-link a { 185 | display: inline-block; 186 | border: 1px solid #4B4B4D; 187 | padding: 3px 5px; 188 | -webkit-border-radius: 3px; 189 | -moz-border-radius: 3px; 190 | border-radius: 3px; 191 | } 192 | 193 | .gh-link a:hover { 194 | background-color: #4B4B4D; 195 | color: #FFFDFD; 196 | } 197 | 198 | .gh-link span { 199 | display: inline-block; 200 | font-size: inherit; 201 | } 202 | 203 | .gh-link span:before { 204 | font: normal normal normal 14px/1 FontAwesome; 205 | content: "\f09b"; 206 | font-size: 26px; 207 | vertical-align: sub; 208 | } 209 | 210 | .todo-link { 211 | font-weight: 600; 212 | text-align: center; 213 | margin: 10px 50px; 214 | } 215 | --------------------------------------------------------------------------------