├── .editorconfig ├── .ember-cli ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .template-lintrc.js ├── .travis.yml ├── .watchmanconfig ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── addon ├── .gitkeep └── mixins │ ├── child-component-support.js │ └── parent-component-support.js ├── app └── .gitkeep ├── config ├── ember-try.js └── environment.js ├── ember-cli-build.js ├── ember-composability.sublime-project ├── index.js ├── package.json ├── renovate.json ├── testem.js ├── tests ├── .eslintrc.js ├── .jshintrc ├── acceptance │ └── index-test.js ├── dummy │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ ├── .gitkeep │ │ │ ├── my-child.js │ │ │ └── my-parent.js │ │ ├── controllers │ │ │ └── .gitkeep │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── index.html │ │ ├── models │ │ │ └── .gitkeep │ │ ├── resolver.js │ │ ├── router.js │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── styles │ │ │ └── app.css │ │ └── templates │ │ │ ├── application.hbs │ │ │ ├── components │ │ │ ├── .gitkeep │ │ │ ├── my-child.hbs │ │ │ └── my-parent.hbs │ │ │ └── index.hbs │ ├── config │ │ ├── environment.js │ │ ├── optional-features.json │ │ └── targets.js │ └── public │ │ ├── crossdomain.xml │ │ └── robots.txt ├── helpers │ ├── destroy-app.js │ ├── module-for-acceptance.js │ ├── resolver.js │ └── start-app.js ├── index.html ├── test-helper.js └── unit │ └── .gitkeep ├── vendor └── .gitkeep └── yarn.lock /.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 | [*.hbs] 17 | insert_final_newline = false 18 | 19 | [*.{diff,md}] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | 12 | # misc 13 | /coverage/ 14 | !.* 15 | 16 | # ember-try 17 | /.node_modules.ember-try/ 18 | /bower.json.ember-try 19 | /package.json.ember-try 20 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | root: true, 4 | parserOptions: { 5 | ecmaVersion: 2017, 6 | sourceType: 'module' 7 | }, 8 | plugins: ['ember'], 9 | extends: ['eslint:recommended', 'plugin:ember/recommended'], 10 | env: { 11 | browser: true 12 | }, 13 | rules: {}, 14 | overrides: [ 15 | // node files 16 | { 17 | files: [ 18 | '.template-lintrc.js', 19 | 'ember-cli-build.js', 20 | 'index.js', 21 | 'testem.js', 22 | 'blueprints/*/index.js', 23 | 'config/**/*.js', 24 | 'tests/dummy/config/**/*.js' 25 | ], 26 | excludedFiles: [ 27 | 'addon/**', 28 | 'addon-test-support/**', 29 | 'app/**', 30 | 'tests/dummy/app/**' 31 | ], 32 | parserOptions: { 33 | sourceType: 'script', 34 | ecmaVersion: 2015 35 | }, 36 | env: { 37 | browser: false, 38 | node: true 39 | }, 40 | plugins: ['node'], 41 | rules: Object.assign( 42 | {}, 43 | require('eslint-plugin-node').configs.recommended.rules, 44 | { 45 | // add your custom rules and overrides for node files here 46 | } 47 | ) 48 | } 49 | ] 50 | }; 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist/ 5 | /tmp/ 6 | 7 | # dependencies 8 | /bower_components/ 9 | /node_modules/ 10 | 11 | # misc 12 | /.sass-cache 13 | /connect.lock 14 | /coverage/ 15 | /libpeerconnection.log 16 | /npm-debug.log* 17 | /testem.log 18 | /yarn-error.log 19 | 20 | # ember-try 21 | /.node_modules.ember-try/ 22 | /bower.json.ember-try 23 | /package.json.ember-try 24 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist/ 3 | /tmp/ 4 | 5 | # dependencies 6 | /bower_components/ 7 | 8 | # misc 9 | /.bowerrc 10 | /.editorconfig 11 | /.ember-cli 12 | /.eslintignore 13 | /.eslintrc.js 14 | /.gitignore 15 | /.template-lintrc.js 16 | /.travis.yml 17 | /.watchmanconfig 18 | /bower.json 19 | /config/ember-try.js 20 | /ember-cli-build.js 21 | /testem.js 22 | /tests/ 23 | /yarn.lock 24 | .gitkeep 25 | 26 | # ember-try 27 | /.node_modules.ember-try/ 28 | /bower.json.ember-try 29 | /package.json.ember-try 30 | -------------------------------------------------------------------------------- /.template-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: 'recommended' 5 | }; 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | # we recommend testing addons with the same minimum supported node version as Ember CLI 4 | # so that your addon works for all apps 5 | - '10' 6 | 7 | sudo: false 8 | dist: trusty 9 | 10 | addons: 11 | chrome: stable 12 | 13 | cache: 14 | yarn: true 15 | 16 | env: 17 | global: 18 | # See https://git.io/vdao3 for details. 19 | - JOBS=1 20 | 21 | stages: 22 | - 'Tests' 23 | - 'Additional Tests' 24 | - 'Canary Tests' 25 | - name: 'Deploy' 26 | if: branch = master AND type = push 27 | 28 | jobs: 29 | fail_fast: true 30 | allow_failures: 31 | - env: EMBER_TRY_SCENARIO=ember-canary 32 | 33 | include: 34 | # runs linting and tests with current locked deps 35 | 36 | - stage: 'Tests' 37 | name: 'Tests' 38 | install: 39 | - yarn install --non-interactive 40 | script: 41 | - ember test 42 | 43 | - name: 'Floating Dependencies' 44 | script: 45 | - ember test 46 | 47 | # we recommend new addons test the current and previous LTS 48 | # as well as latest stable release (bonus points to beta/canary) 49 | - stage: 'Additional Tests' 50 | env: EMBER_TRY_SCENARIO=ember-lts-2.16 51 | - env: EMBER_TRY_SCENARIO=ember-lts-2.18 52 | - env: EMBER_TRY_SCENARIO=ember-release 53 | - env: EMBER_TRY_SCENARIO=ember-beta 54 | - env: EMBER_TRY_SCENARIO=ember-default-with-jquery 55 | 56 | - stage: 'Deploy' 57 | name: 'Publish to npm' 58 | install: 59 | - yarn install --non-interactive 60 | script: yarn semantic-release 61 | 62 | before_install: 63 | - curl -o- -L https://yarnpkg.com/install.sh | bash 64 | - export PATH=$HOME/.yarn/bin:$PATH 65 | 66 | install: 67 | - yarn install --no-lockfile --non-interactive 68 | 69 | script: 70 | - node_modules/.bin/ember try:one $EMBER_TRY_SCENARIO 71 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.0.1](https://github.com/mike-north/ember-composability/compare/v1.0.0...v1.0.1) (2019-01-15) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * use [@ember](https://github.com/ember)/ordered-set instead of private API (fixes [#114](https://github.com/mike-north/ember-composability/issues/114)) ([ff01588](https://github.com/mike-north/ember-composability/commit/ff01588)) 7 | * use notifyPropertyChange instead of propertyDidChange (fixes [#89](https://github.com/mike-north/ember-composability/issues/89)) ([d47bc69](https://github.com/mike-north/ember-composability/commit/d47bc69)) 8 | 9 | # [1.0.0](https://github.com/mike-north/ember-composability/compare/v0.4.0...v1.0.0) (2018-11-10) 10 | 11 | 12 | ### chore 13 | 14 | * remove unused code, touchup README ([4b8a93f](https://github.com/mike-north/ember-composability/commit/4b8a93f)) 15 | 16 | 17 | ### BREAKING CHANGES 18 | 19 | * declare 1.0 release 20 | 21 | # [0.4.0](https://github.com/mike-north/ember-composability/compare/v0.3.8...v0.4.0) (2018-11-10) 22 | 23 | 24 | ### Bug Fixes 25 | 26 | * don't require ember-canary to pass tests ([fae0bc7](https://github.com/mike-north/ember-composability/commit/fae0bc7)) 27 | * don't require ember-canary to pass tests ([c92ce49](https://github.com/mike-north/ember-composability/commit/c92ce49)) 28 | 29 | 30 | ### Features 31 | 32 | * modernization ([00a949c](https://github.com/mike-north/ember-composability/commit/00a949c)) 33 | 34 | # Change Log 35 | 36 | ## [v0.0.4](https://github.com/truenorth/ember-composability/tree/v0.0.4) (2015-07-11) 37 | 38 | [Full Changelog](https://github.com/truenorth/ember-composability/compare/v0.0.3...v0.0.4) 39 | 40 | **Merged pull requests:** 41 | 42 | - Remove ember-data dependency [\#1](https://github.com/truenorth/ember-composability/pull/1) ([truenorth](https://github.com/truenorth)) 43 | 44 | ## [v0.0.3](https://github.com/truenorth/ember-composability/tree/v0.0.3) (2015-07-11) 45 | 46 | [Full Changelog](https://github.com/truenorth/ember-composability/compare/v0.0.2...v0.0.3) 47 | 48 | ## [v0.0.2](https://github.com/truenorth/ember-composability/tree/v0.0.2) (2015-07-11) 49 | 50 | 51 | 52 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* 53 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Mike North 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ember-composability 2 | 3 | [![Build Status](https://travis-ci.org/mike-north/ember-composability.svg?branch=master)](https://travis-ci.org/mike-north/ember-composability) 4 | [![npm version](https://img.shields.io/npm/v/ember-composability.svg)](https://www.npmjs.com/package/ember-composability) 5 | 6 | Composability-oriented tools for Ember.js apps 7 | 8 | ## Composable components 9 | 10 | The `child-component-support` and `parent-component-support` mixins can be used for parents and children that need aware ness and/or access to each other 11 | 12 | For example, you may want to expressively declare some parent/child components like this 13 | 14 | ```handlebars 15 | {{#my-parent}} 16 | {{my-child}} 17 | {{my-child}} 18 | {{my-child}} 19 | {{/my-parent}} 20 | 21 | ``` 22 | 23 | #### Parent 24 | 25 | **app/components/my-parent.js** 26 | 27 | ```js 28 | import Ember from 'ember'; 29 | import ParentComponentSupport from 'ember-composability/mixins/parent-component-support'; 30 | import layout from '../templates/components/my-parent'; 31 | 32 | export default Ember.Component.extend(ParentComponentSupport, { 33 | name: 'mike', 34 | layout 35 | }); 36 | ``` 37 | 38 | parents can have access to child properties, via the `composableChildren` property 39 | 40 | **app/components/my-parent.js** 41 | 42 | ```javascript 43 | totalValue: computed('composableChildren.@each.value', { 44 | get() { 45 | return this.get('composableChildren').reduce( 46 | (acc, val) => (acc += val.get('value')), 47 | 0 48 | ); 49 | } 50 | }); 51 | ``` 52 | 53 | #### Child 54 | 55 | **app/components/my-child.js** 56 | 57 | ```js 58 | import Ember from 'ember'; 59 | import ChildComponentSupport from 'ember-composability/mixins/child-component-support'; 60 | import MyParent from './my-parent'; 61 | import layout from '../templates/components/my-child'; 62 | 63 | export default Ember.Component.extend(ChildComponentSupport, { 64 | value: 3, 65 | layout, 66 | _parentComponentTypes: [MyParent] 67 | }); 68 | ``` 69 | 70 | children can have access to parent properties via the `composableParent` property 71 | 72 | **app/templates/components/my-child.hbs** 73 | 74 | ```handlebars 75 | {{composableParent.name}} 76 | 77 | ``` 78 | 79 | **By default, all children will be registered with their parent. If you'd like to customize which components are registered, override the `shouldRegisterToParent` method:** 80 | 81 | ```js 82 | shouldRegisterToParent(parentComponent) { 83 | const registeredChildren = parentComponent.getComposableChildren(); 84 | const existingChild = childComponents.findBy('headerName', this.get('headerName')); 85 | return Ember.isNone(existingChild); 86 | } 87 | ``` 88 | 89 | ## Installation 90 | 91 | - `git clone` this repository 92 | - `npm install` 93 | - `bower install` 94 | 95 | ## Running 96 | 97 | - `ember server` 98 | - Visit your app at http://localhost:4200. 99 | 100 | ## Running Tests 101 | 102 | - `ember test` 103 | - `ember test --server` 104 | 105 | ## Building 106 | 107 | - `ember build` 108 | 109 | For more information on using ember-cli, visit [http://www.ember-cli.com/](http://www.ember-cli.com/). 110 | 111 | ![Analytics](https://ga-beacon.appspot.com/UA-66610985-1/mike-north/ember-composability/readme) 112 | -------------------------------------------------------------------------------- /addon/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-composability/65c51b423261fbb01d0ea11beabdc85bfed0b4be/addon/.gitkeep -------------------------------------------------------------------------------- /addon/mixins/child-component-support.js: -------------------------------------------------------------------------------- 1 | import { assert } from '@ember/debug'; 2 | import { computed } from '@ember/object'; 3 | import Mixin from '@ember/object/mixin'; 4 | 5 | export default Mixin.create({ 6 | init() { 7 | this._super(...arguments); 8 | assert( 9 | 'Must define _parentComponentTypes', 10 | this.get('_parentComponentTypes') 11 | ); 12 | this._registerWithParent(); 13 | }, 14 | 15 | willDestroyElement() { 16 | this._unregisterWithParent(); 17 | this._super(...arguments); 18 | }, 19 | 20 | composableParent: computed(function() { 21 | return this._componentToRegisterTo(); 22 | }), 23 | 24 | _componentToRegisterTo() { 25 | let c = null; 26 | let parentTypes = this.get('_parentComponentTypes'); 27 | for (let i = 0; i < parentTypes.length && !c; i++) { 28 | c = this.nearestOfType(parentTypes[i]); 29 | } 30 | return c; 31 | }, 32 | 33 | shouldRegisterToParent(/* parentComponent*/) { 34 | return true; 35 | }, 36 | 37 | _registerWithParent() { 38 | let parentComponent = this._componentToRegisterTo(); 39 | if (parentComponent) { 40 | if (this.shouldRegisterToParent(parentComponent)) { 41 | parentComponent.registerChildComponent(this); 42 | } 43 | this.set('composableParent', parentComponent); 44 | } 45 | }, 46 | 47 | _unregisterWithParent() { 48 | let parentComponent = this._componentToRegisterTo(); 49 | if (parentComponent) { 50 | parentComponent.unregisterChildComponent(this); 51 | } 52 | } 53 | }); 54 | -------------------------------------------------------------------------------- /addon/mixins/parent-component-support.js: -------------------------------------------------------------------------------- 1 | import { A } from '@ember/array'; 2 | import { computed } from '@ember/object'; 3 | import Mixin from '@ember/object/mixin'; 4 | import { debounce } from '@ember/runloop'; 5 | import OrderedSet from '@ember/ordered-set'; 6 | 7 | export default Mixin.create({ 8 | _childComponents: null, 9 | composableChildrenDebounceTime: 0, 10 | 11 | init() { 12 | this._super(...arguments); 13 | this.set('_childComponents', new OrderedSet()); 14 | }, 15 | 16 | composableChildren: computed(function() { 17 | return this.getComposableChildren(); 18 | }).readOnly(), 19 | 20 | getComposableChildren() { 21 | let comps = this.get('_childComponents'); 22 | return new A(comps && comps.size ? this.get('_childComponents').list : []); 23 | }, 24 | 25 | _fireComposableChildrenChanged() { 26 | if (typeof this.notifyPropertyChange === 'function') { 27 | this.notifyPropertyChange('composableChildren'); 28 | } 29 | else if (typeof this.propertyDidChange === 'function') { 30 | // Deprecated in ember 3.1 31 | this.propertyDidChange('composableChildren'); 32 | } 33 | else { 34 | throw new Error('Unable to call notifyPropertyChange'); 35 | } 36 | }, 37 | 38 | _notifyComposableChildrenChanged() { 39 | if (this.get('composableChildrenDebounceTime')) { 40 | debounce(this, this._fireComposableChildrenChanged, this.get('composableChildrenDebounceTime')); 41 | } else { 42 | this._fireComposableChildrenChanged(); 43 | } 44 | }, 45 | 46 | registerChildComponent(childComponent) { 47 | this.get('_childComponents').add(childComponent); 48 | this._notifyComposableChildrenChanged(); 49 | }, 50 | 51 | unregisterChildComponent(childComponent) { 52 | this.get('_childComponents').delete(childComponent); 53 | this._notifyComposableChildrenChanged(); 54 | } 55 | }); 56 | -------------------------------------------------------------------------------- /app/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-composability/65c51b423261fbb01d0ea11beabdc85bfed0b4be/app/.gitkeep -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getChannelURL = require('ember-source-channel-url'); 4 | 5 | module.exports = function() { 6 | return Promise.all([ 7 | getChannelURL('release'), 8 | getChannelURL('beta'), 9 | getChannelURL('canary') 10 | ]).then((urls) => { 11 | return { 12 | useYarn: true, 13 | scenarios: [ 14 | { 15 | name: 'ember-lts-2.16', 16 | env: { 17 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 'jquery-integration': true }), 18 | }, 19 | npm: { 20 | devDependencies: { 21 | '@ember/jquery': '^0.5.1', 22 | 'ember-source': '~2.16.0' 23 | } 24 | } 25 | }, 26 | { 27 | name: 'ember-lts-2.18', 28 | env: { 29 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 'jquery-integration': true }), 30 | }, 31 | npm: { 32 | devDependencies: { 33 | '@ember/jquery': '^0.5.1', 34 | 'ember-source': '~2.18.0' 35 | } 36 | } 37 | }, 38 | { 39 | name: 'ember-release', 40 | npm: { 41 | devDependencies: { 42 | 'ember-source': urls[0] 43 | } 44 | } 45 | }, 46 | { 47 | name: 'ember-beta', 48 | npm: { 49 | devDependencies: { 50 | 'ember-source': urls[1] 51 | } 52 | } 53 | }, 54 | { 55 | name: 'ember-canary', 56 | npm: { 57 | devDependencies: { 58 | 'ember-source': urls[2] 59 | } 60 | } 61 | }, 62 | { 63 | name: 'ember-default', 64 | npm: { 65 | devDependencies: {} 66 | } 67 | }, 68 | { 69 | name: 'ember-default-with-jquery', 70 | env: { 71 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 72 | 'jquery-integration': true 73 | }) 74 | }, 75 | npm: { 76 | devDependencies: { 77 | '@ember/jquery': '^0.5.1' 78 | } 79 | } 80 | } 81 | ] 82 | }; 83 | }); 84 | }; 85 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(/* environment, appConfig */) { 4 | return { }; 5 | }; 6 | -------------------------------------------------------------------------------- /ember-cli-build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 4 | 5 | module.exports = function(defaults) { 6 | let app = new EmberAddon(defaults, { 7 | // Add options here 8 | }); 9 | 10 | /* 11 | This build file specifies the options for the dummy test app of this 12 | addon, located in `/tests/dummy` 13 | This build file does *not* influence how the addon or the app using it 14 | behave. You most likely want to be modifying `./index.js` or app's build file 15 | */ 16 | 17 | return app.toTree(); 18 | }; 19 | -------------------------------------------------------------------------------- /ember-composability.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "path": ".", 6 | "folder_exclude_patterns": ["tmp", "node_modules", "bower_components"] 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | name: require('./package').name 5 | }; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-composability", 3 | "version": "0.0.0-development", 4 | "description": "Ember.js composability tools & helpers", 5 | "directories": { 6 | "doc": "doc", 7 | "test": "tests" 8 | }, 9 | "scripts": { 10 | "build": "ember build", 11 | "start": "ember server", 12 | "test": "ember try:each", 13 | "semantic-release": "semantic-release" 14 | }, 15 | "repository": "https://github.com/mike-north/ember-composability.git", 16 | "engines": { 17 | "node": "6.* || 8.* || >= 10.*" 18 | }, 19 | "homepage": "https://github.com/mike-north/ember-composability", 20 | "author": "Mike North (http://mike.works)", 21 | "license": "MIT", 22 | "devDependencies": { 23 | "@commitlint/cli": "7.6.1", 24 | "@commitlint/config-conventional": "7.6.0", 25 | "@commitlint/travis-cli": "7.6.1", 26 | "@ember/optional-features": "0.7.0", 27 | "@mike-north/js-lib-renovate-config": "1.3.1", 28 | "@mike-north/js-lib-semantic-release-config": "1.0.1", 29 | "@types/ember": "3.0.29", 30 | "broccoli-asset-rev": "3.0.0", 31 | "ember-cli": "4.0.1", 32 | "ember-cli-dependency-checker": "3.2.0", 33 | "ember-cli-eslint": "5.1.0", 34 | "ember-cli-htmlbars": "6.0.1", 35 | "ember-cli-htmlbars-inline-precompile": "3.0.2", 36 | "ember-cli-inject-live-reload": "2.1.0", 37 | "ember-cli-qunit": "4.4.0", 38 | "ember-cli-sri": "2.1.1", 39 | "ember-cli-template-lint": "1.0.0", 40 | "ember-cli-uglify": "3.0.0", 41 | "ember-disable-prototype-extensions": "1.1.3", 42 | "ember-export-application-global": "2.0.1", 43 | "ember-load-initializers": "2.1.2", 44 | "ember-maybe-import-regenerator": "0.1.6", 45 | "ember-resolver": "8.0.3", 46 | "ember-source": "4.0.1", 47 | "ember-source-channel-url": "1.2.0", 48 | "ember-try": "1.2.1", 49 | "eslint-plugin-ember": "10.5.8", 50 | "eslint-plugin-node": "11.1.0", 51 | "husky": "1.3.1", 52 | "loader.js": "4.7.0", 53 | "qunit-dom": "2.0.0", 54 | "semantic-release": "15.12.5" 55 | }, 56 | "keywords": [ 57 | "ember-addon", 58 | "composable", 59 | "composability", 60 | "component" 61 | ], 62 | "dependencies": { 63 | "ember-cli-babel": "^7.0.0", 64 | "@ember/ordered-set": "^2.0.3" 65 | }, 66 | "ember-addon": { 67 | "configPath": "tests/dummy/config" 68 | }, 69 | "commitlint": { 70 | "extends": [ 71 | "@commitlint/config-conventional" 72 | ] 73 | }, 74 | "husky": { 75 | "hooks": { 76 | "commit-msg": "./node_modules/.bin/commitlint -e $HUSKY_GIT_PARAMS" 77 | } 78 | }, 79 | "release": { 80 | "extends": "@mike-north/js-lib-semantic-release-config" 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@mike-north/js-lib-renovate-config"] 3 | } 4 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | test_page: 'tests/index.html?hidepassed', 3 | disable_watching: true, 4 | launch_in_ci: [ 5 | 'Chrome' 6 | ], 7 | launch_in_dev: [ 8 | 'Chrome' 9 | ], 10 | browser_args: { 11 | Chrome: { 12 | ci: [ 13 | // --no-sandbox is needed when running Chrome inside a container 14 | process.env.CI ? '--no-sandbox' : null, 15 | '--headless', 16 | '--disable-gpu', 17 | '--disable-dev-shm-usage', 18 | '--disable-software-rasterizer', 19 | '--mute-audio', 20 | '--remote-debugging-port=0', 21 | '--window-size=1440,900' 22 | ].filter(Boolean) 23 | } 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /tests/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | env: { 4 | embertest: true 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /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 | ], 26 | "node": false, 27 | "browser": false, 28 | "boss": true, 29 | "curly": true, 30 | "debug": false, 31 | "devel": false, 32 | "eqeqeq": true, 33 | "evil": true, 34 | "forin": false, 35 | "immed": false, 36 | "laxbreak": false, 37 | "newcap": true, 38 | "noarg": true, 39 | "noempty": false, 40 | "nonew": false, 41 | "nomen": false, 42 | "onevar": false, 43 | "plusplus": false, 44 | "regexp": false, 45 | "undef": true, 46 | "sub": true, 47 | "strict": false, 48 | "white": false, 49 | "eqnull": true, 50 | "esnext": true, 51 | "unused": true 52 | } 53 | -------------------------------------------------------------------------------- /tests/acceptance/index-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { visit, currentURL, findAll } from '@ember/test-helpers'; 3 | import { setupApplicationTest } from 'ember-qunit'; 4 | 5 | module('Acceptance | index', function(hooks) { 6 | setupApplicationTest(hooks); 7 | 8 | test('visiting /index', async function(assert) { 9 | await visit('/'); 10 | 11 | assert.equal(currentURL(), '/'); 12 | // Block form, parent with children yielded 13 | assert.equal(currentURL(), '/'); 14 | assert.equal( 15 | findAll('.block-children .num-child-components')[0].innerText, 16 | '3', 17 | 'Correct number of child components registered with parent' 18 | ); 19 | assert.equal( 20 | findAll('.block-children .child .parent-name')[0].innerText.trim(), 21 | 'lolparent', 22 | 'Child component can access parent properties' 23 | ); 24 | 25 | // Child alone 26 | assert.equal( 27 | findAll('.child-alone')[0].innerText.trim(), 28 | 'child', 29 | 'Child component renders alone' 30 | ); 31 | assert.equal( 32 | findAll('.child-alone .child .parent-name')[0].innerText.trim(), 33 | '', 34 | 'Child component parent properties are empty' 35 | ); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import Resolver from './resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from './config/environment'; 5 | 6 | const App = Application.extend({ 7 | modulePrefix: config.modulePrefix, 8 | podModulePrefix: config.podModulePrefix, 9 | Resolver 10 | }); 11 | 12 | loadInitializers(App, config.modulePrefix); 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-composability/65c51b423261fbb01d0ea11beabdc85bfed0b4be/tests/dummy/app/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/components/my-child.js: -------------------------------------------------------------------------------- 1 | import Component from '@ember/component'; 2 | import ChildComponentSupport from 'ember-composability/mixins/child-component-support'; 3 | import MyParent from './my-parent'; 4 | import layout from '../templates/components/my-child'; 5 | 6 | export default Component.extend(ChildComponentSupport, { 7 | classNames: ['bordered-container', 'child'], 8 | layout, 9 | value: 3, 10 | // eslint-disable-next-line ember/avoid-leaking-state-in-ember-objects 11 | _parentComponentTypes: [MyParent] 12 | }); 13 | -------------------------------------------------------------------------------- /tests/dummy/app/components/my-parent.js: -------------------------------------------------------------------------------- 1 | import Component from '@ember/component'; 2 | import { computed } from '@ember/object'; 3 | import ParentComponentSupport from 'ember-composability/mixins/parent-component-support'; 4 | import layout from '../templates/components/my-parent'; 5 | 6 | export default Component.extend(ParentComponentSupport, { 7 | classNames: ['bordered-container', 'parent'], 8 | composableChildrenDebounceTime: 1, 9 | layout, 10 | name: 'lolparent', 11 | 12 | totalValue: computed('composableChildren.@each.value', function() { 13 | return this.get('composableChildren').reduce((acc, val) => acc += val.get('value'), 0); 14 | }) 15 | }); 16 | -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-composability/65c51b423261fbb01d0ea11beabdc85bfed0b4be/tests/dummy/app/controllers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-composability/65c51b423261fbb01d0ea11beabdc85bfed0b4be/tests/dummy/app/helpers/.gitkeep -------------------------------------------------------------------------------- /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 | {{content-for "body-footer"}} 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-composability/65c51b423261fbb01d0ea11beabdc85bfed0b4be/tests/dummy/app/models/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from 'ember-resolver'; 2 | 3 | export default Resolver; 4 | -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@ember/routing/router'; 2 | import config from './config/environment'; 3 | 4 | const Router = EmberRouter.extend({ 5 | location: config.locationType, 6 | rootURL: config.rootURL 7 | }); 8 | 9 | Router.map(function() { 10 | }); 11 | 12 | export default Router; 13 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-composability/65c51b423261fbb01d0ea11beabdc85bfed0b4be/tests/dummy/app/routes/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- 1 | .bordered-container { 2 | border: 1px solid black; 3 | margin: 8px; 4 | padding: 8px; 5 | } 6 | 7 | 8 | .bordered-container.parent { 9 | background-color: rgba(255,0,0,0.2); 10 | } 11 | 12 | .bordered-container.child { 13 | background-color: rgba(0, 0, 255,0.2); 14 | } 15 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 |

Welcome to Ember

2 | 3 | {{outlet}} 4 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-composability/65c51b423261fbb01d0ea11beabdc85bfed0b4be/tests/dummy/app/templates/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/my-child.hbs: -------------------------------------------------------------------------------- 1 | child 2 | {{yield}} 3 |
{{composableParent.name}}
4 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/my-parent.hbs: -------------------------------------------------------------------------------- 1 | parent 2 | {{totalValue}} 3 |
{{composableChildren.length}}
4 | {{yield}} -------------------------------------------------------------------------------- /tests/dummy/app/templates/index.hbs: -------------------------------------------------------------------------------- 1 |
2 |

Parent with children

3 |
4 | {{#my-parent}} 5 | {{my-child}} 6 | {{my-child}} 7 | {{my-child}} 8 | {{/my-parent}} 9 |
10 |
11 | 12 |
13 |

Child alone

14 |
15 | {{my-child}} 16 |
17 |
18 | -------------------------------------------------------------------------------- /tests/dummy/config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(environment) { 4 | let ENV = { 5 | modulePrefix: 'dummy', 6 | environment, 7 | rootURL: '/', 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 | EXTEND_PROTOTYPES: { 15 | // Prevent Ember Data from overriding Date.parse. 16 | Date: false 17 | } 18 | }, 19 | 20 | APP: { 21 | // Here you can pass flags/options to your application instance 22 | // when it is created 23 | } 24 | }; 25 | 26 | if (environment === 'development') { 27 | // ENV.APP.LOG_RESOLVER = true; 28 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 29 | // ENV.APP.LOG_TRANSITIONS = true; 30 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 31 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 32 | } 33 | 34 | if (environment === 'test') { 35 | // Testem prefers this... 36 | ENV.locationType = 'none'; 37 | 38 | // keep test console output quieter 39 | ENV.APP.LOG_ACTIVE_GENERATION = false; 40 | ENV.APP.LOG_VIEW_LOOKUPS = false; 41 | 42 | ENV.APP.rootElement = '#ember-testing'; 43 | ENV.APP.autoboot = false; 44 | } 45 | 46 | if (environment === 'production') { 47 | // here you can enable a production-specific feature 48 | } 49 | 50 | return ENV; 51 | }; 52 | -------------------------------------------------------------------------------- /tests/dummy/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "jquery-integration": false 3 | } 4 | -------------------------------------------------------------------------------- /tests/dummy/config/targets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const browsers = [ 4 | 'last 1 Chrome versions', 5 | 'last 1 Firefox versions', 6 | 'last 1 Safari versions' 7 | ]; 8 | 9 | const isCI = !!process.env.CI; 10 | const isProduction = process.env.EMBER_ENV === 'production'; 11 | 12 | if (isCI || isProduction) { 13 | browsers.push('ie 11'); 14 | } 15 | 16 | module.exports = { 17 | browsers 18 | }; 19 | -------------------------------------------------------------------------------- /tests/dummy/public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /tests/helpers/destroy-app.js: -------------------------------------------------------------------------------- 1 | import { run } from '@ember/runloop'; 2 | 3 | export default function destroyApp(application) { 4 | run(application, 'destroy'); 5 | } 6 | -------------------------------------------------------------------------------- /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 | import RSVP from 'rsvp'; 5 | 6 | export default function(name, options = {}) { 7 | module(name, { 8 | beforeEach() { 9 | this.application = startApp(); 10 | 11 | if (options.beforeEach) { 12 | return options.beforeEach.apply(this, arguments); 13 | } 14 | }, 15 | 16 | afterEach() { 17 | let afterEach = 18 | options.afterEach && options.afterEach.apply(this, arguments); 19 | return RSVP.Promise.resolve(afterEach).then(() => 20 | destroyApp(this.application) 21 | ); 22 | } 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/helpers/start-app.js: -------------------------------------------------------------------------------- 1 | import Application from '../../app'; 2 | import config from '../../config/environment'; 3 | import { merge } from '@ember/polyfills'; 4 | import { run } from '@ember/runloop'; 5 | 6 | export default function startApp(attrs) { 7 | let attributes = merge({}, config.APP); 8 | attributes = merge(attributes, attrs); // use defaults, but you can override; 9 | 10 | return run(() => { 11 | let application = Application.create(attributes); 12 | application.setupForTesting(); 13 | application.injectTestHelpers(); 14 | return application; 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /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 | {{content-for "body-footer"}} 31 | {{content-for "test-body-footer"}} 32 | 33 | 34 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import Application from '../app'; 2 | import config from '../config/environment'; 3 | import { setApplication } from '@ember/test-helpers'; 4 | import { start } from 'ember-qunit'; 5 | 6 | setApplication(Application.create(config.APP)); 7 | 8 | start(); 9 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-composability/65c51b423261fbb01d0ea11beabdc85bfed0b4be/tests/unit/.gitkeep -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-composability/65c51b423261fbb01d0ea11beabdc85bfed0b4be/vendor/.gitkeep --------------------------------------------------------------------------------