├── .editorconfig ├── .ember-cli ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .template-lintrc.js ├── .travis.yml ├── .watchmanconfig ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── addon ├── components │ └── ember-wormhole.js ├── templates │ └── components │ │ └── ember-wormhole.hbs └── utils │ └── dom.js ├── app └── components │ └── ember-wormhole.js ├── config ├── ember-try.js └── environment.js ├── ember-cli-build.js ├── index.js ├── package.json ├── testem.js ├── tests ├── acceptance │ └── wormhole-test.js ├── dummy │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ ├── .gitkeep │ │ │ ├── current-user.js │ │ │ ├── document-title.js │ │ │ ├── ember-wormhole.js │ │ │ └── x-favicon.js │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ └── index.js │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── index.html │ │ ├── initializers │ │ │ └── add-modals-element.js │ │ ├── models │ │ │ └── .gitkeep │ │ ├── router.js │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── styles │ │ │ └── app.css │ │ ├── templates │ │ │ ├── application.hbs │ │ │ ├── components │ │ │ │ ├── current-user.hbs │ │ │ │ └── x-favicon.hbs │ │ │ ├── index.hbs │ │ │ ├── inplace.hbs │ │ │ └── wormhole.hbs │ │ └── utils │ │ │ └── data.js │ ├── config │ │ ├── ember-cli-update.json │ │ ├── environment.js │ │ ├── optional-features.json │ │ └── targets.js │ └── public │ │ └── robots.txt ├── fastboot │ ├── inplace-test.js │ └── wormhole-test.js ├── helpers │ ├── .gitkeep │ └── resolver.js ├── index.html ├── integration │ └── components │ │ └── ember-wormhole-test.js ├── 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 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.hbs] 16 | insert_final_newline = false 17 | 18 | [*.{diff,md}] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /.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 | /node_modules/ 12 | 13 | # misc 14 | /coverage/ 15 | !.* 16 | 17 | # ember-try 18 | /.node_modules.ember-try/ 19 | /bower.json.ember-try 20 | /package.json.ember-try 21 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | root: true, 5 | parser: 'babel-eslint', 6 | parserOptions: { 7 | ecmaVersion: 2018, 8 | sourceType: 'module', 9 | ecmaFeatures: { 10 | legacyDecorators: true 11 | } 12 | }, 13 | plugins: [ 14 | 'ember' 15 | ], 16 | extends: [ 17 | 'eslint:recommended', 18 | 'plugin:ember/recommended' 19 | ], 20 | env: { 21 | browser: true 22 | }, 23 | rules: { 24 | 'ember/no-jquery': 'off', 25 | 'ember/no-get': 'off', 26 | 'ember/no-global-jquery': 'off', 27 | 'ember/no-observers': 'off', 28 | 'ember/no-on-calls-in-components': 'off', 29 | 'ember/require-computed-property-dependencies': 'off' 30 | }, 31 | overrides: [ 32 | // node files 33 | { 34 | files: [ 35 | '.eslintrc.js', 36 | '.template-lintrc.js', 37 | 'ember-cli-build.js', 38 | 'index.js', 39 | 'testem.js', 40 | 'blueprints/*/index.js', 41 | 'config/**/*.js', 42 | 'tests/dummy/config/**/*.js' 43 | ], 44 | excludedFiles: [ 45 | 'addon/**', 46 | 'addon-test-support/**', 47 | 'app/**', 48 | 'tests/dummy/app/**' 49 | ], 50 | parserOptions: { 51 | sourceType: 'script' 52 | }, 53 | env: { 54 | es6: 2018, 55 | browser: false, 56 | node: true, 57 | mocha: true 58 | }, 59 | plugins: ['node'], 60 | extends: ['plugin:node/recommended'] 61 | } 62 | ] 63 | }; 64 | -------------------------------------------------------------------------------- /.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 | /.env* 13 | /.pnp* 14 | /.sass-cache 15 | /connect.lock 16 | /coverage/ 17 | /libpeerconnection.log 18 | /npm-debug.log* 19 | /testem.log 20 | /yarn-error.log 21 | 22 | # ember-try 23 | /.node_modules.ember-try/ 24 | /bower.json.ember-try 25 | /package.json.ember-try 26 | -------------------------------------------------------------------------------- /.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 | /.env* 13 | /.eslintignore 14 | /.eslintrc.js 15 | /.git/ 16 | /.gitignore 17 | /.template-lintrc.js 18 | /.travis.yml 19 | /.watchmanconfig 20 | /bower.json 21 | /config/ember-try.js 22 | /CONTRIBUTING.md 23 | /ember-cli-build.js 24 | /testem.js 25 | /tests/ 26 | /yarn.lock 27 | .gitkeep 28 | 29 | # ember-try 30 | /.node_modules.ember-try/ 31 | /bower.json.ember-try 32 | /package.json.ember-try 33 | -------------------------------------------------------------------------------- /.template-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: 'recommended', 5 | rules: { 6 | 'no-unbound': false 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | node_js: 4 | # we recommend testing addons with the same minimum supported node version as Ember CLI 5 | # so that your addon works for all apps 6 | - "10" 7 | 8 | dist: xenial 9 | 10 | addons: 11 | chrome: stable 12 | 13 | cache: 14 | directories: 15 | - $HOME/.npm 16 | 17 | env: 18 | global: 19 | # See https://git.io/vdao3 for details. 20 | - JOBS=1 21 | 22 | branches: 23 | only: 24 | - master 25 | # npm version tags 26 | - /^v\d+\.\d+\.\d+/ 27 | 28 | jobs: 29 | fast_finish: true 30 | allow_failures: 31 | - env: EMBER_TRY_SCENARIO=ember-canary 32 | 33 | include: 34 | # runs linting and tests with current locked deps 35 | - stage: "Tests" 36 | name: "Tests" 37 | script: 38 | - yarn run lint 39 | - yarn run test:ember 40 | 41 | - stage: "Additional Tests" 42 | name: "Floating Dependencies" 43 | install: 44 | - yarn install --no-lockfile --non-interactive 45 | script: 46 | - yarn run test:ember 47 | 48 | # we recommend new addons test the current and previous LTS 49 | # as well as latest stable release (bonus points to beta/canary) 50 | - env: EMBER_TRY_SCENARIO=ember-lts-3.16 51 | - env: EMBER_TRY_SCENARIO=ember-lts-3.20 52 | - env: EMBER_TRY_SCENARIO=ember-release 53 | - env: EMBER_TRY_SCENARIO=ember-beta 54 | - env: EMBER_TRY_SCENARIO=ember-canary 55 | - env: EMBER_TRY_SCENARIO=ember-default-with-jquery 56 | - env: EMBER_TRY_SCENARIO=ember-classic 57 | 58 | script: 59 | - node_modules/.bin/ember try:one $EMBER_TRY_SCENARIO 60 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [0.5.4](https://github.com/yapplabs/ember-wormhole/tree/0.5.4) (2018-09-20) 4 | [Full Changelog](https://github.com/yapplabs/ember-wormhole/compare/0.5.4...0.5.4) 5 | 6 | **Merged pull requests:** 7 | 8 | - Fix \#104 Toggling renderInPlace [\#107](https://github.com/yapplabs/ember-wormhole/pull/107) ([dnachev](https://github.com/dnachev)) 9 | 10 | ## [0.5.4](https://github.com/yapplabs/ember-wormhole/tree/0.5.4) (2017-12-09) 11 | [Full Changelog](https://github.com/yapplabs/ember-wormhole/compare/0.5.3...0.5.4) 12 | 13 | **Merged pull requests:** 14 | 15 | - Fix CI [\#102](https://github.com/yapplabs/ember-wormhole/pull/102) ([cibernox](https://github.com/cibernox)) 16 | - \[BUGFIX\] If `renderInPlace` is true, destinationElement is ignored [\#101](https://github.com/yapplabs/ember-wormhole/pull/101) ([cibernox](https://github.com/cibernox)) 17 | 18 | ## [0.5.3](https://github.com/yapplabs/ember-wormhole/tree/0.5.3) (2017-11-22) 19 | [Full Changelog](https://github.com/yapplabs/ember-wormhole/compare/0.5.2...0.5.3) 20 | 21 | **Merged pull requests:** 22 | 23 | - Update ember-cli and dependencies. Use ember modules. [\#100](https://github.com/yapplabs/ember-wormhole/pull/100) ([btecu](https://github.com/btecu)) 24 | - Add FastBoot tests [\#99](https://github.com/yapplabs/ember-wormhole/pull/99) ([simonihmig](https://github.com/simonihmig)) 25 | - Use "New Module Imports" [\#98](https://github.com/yapplabs/ember-wormhole/pull/98) ([Turbo87](https://github.com/Turbo87)) 26 | - ember-cli 2.14 updates [\#97](https://github.com/yapplabs/ember-wormhole/pull/97) ([Dhaulagiri](https://github.com/Dhaulagiri)) 27 | - Fix wormhole insertion in FastBoot [\#96](https://github.com/yapplabs/ember-wormhole/pull/96) ([sandydoo](https://github.com/sandydoo)) 28 | 29 | ## [0.5.2](https://github.com/yapplabs/ember-wormhole/tree/0.5.2) (2017-06-12) 30 | [Full Changelog](https://github.com/yapplabs/ember-wormhole/compare/0.5.1...0.5.2) 31 | 32 | **Merged pull requests:** 33 | 34 | - Fix getDOM for Ember 2.6/2.7 in integration tests [\#95](https://github.com/yapplabs/ember-wormhole/pull/95) ([simonihmig](https://github.com/simonihmig)) 35 | - Upgrade to Ember-cli 2.13 / Babel6 / yarn [\#94](https://github.com/yapplabs/ember-wormhole/pull/94) ([simonihmig](https://github.com/simonihmig)) 36 | - Update code sample to use boolean instead of string representation of a boolean [\#90](https://github.com/yapplabs/ember-wormhole/pull/90) ([andrewhavens](https://github.com/andrewhavens)) 37 | - some cleanup and clarifying [\#85](https://github.com/yapplabs/ember-wormhole/pull/85) ([raycohen](https://github.com/raycohen)) 38 | - Add note about Ember 2.10 [\#83](https://github.com/yapplabs/ember-wormhole/pull/83) ([lukemelia](https://github.com/lukemelia)) 39 | - Update README.md [\#80](https://github.com/yapplabs/ember-wormhole/pull/80) ([graham-sportsmgmt](https://github.com/graham-sportsmgmt)) 40 | - Update README.md [\#79](https://github.com/yapplabs/ember-wormhole/pull/79) ([graham-sportsmgmt](https://github.com/graham-sportsmgmt)) 41 | 42 | ## [0.5.1](https://github.com/yapplabs/ember-wormhole/tree/0.5.1) (2016-11-10) 43 | [Full Changelog](https://github.com/yapplabs/ember-wormhole/compare/0.5.0...0.5.1) 44 | 45 | **Merged pull requests:** 46 | 47 | - Fix some issues with Glimmer2 usage. [\#75](https://github.com/yapplabs/ember-wormhole/pull/75) ([rwjblue](https://github.com/rwjblue)) 48 | 49 | ## [0.5.0](https://github.com/yapplabs/ember-wormhole/tree/0.5.0) (2016-10-18) 50 | [Full Changelog](https://github.com/yapplabs/ember-wormhole/compare/0.4.1...0.5.0) 51 | 52 | **Merged pull requests:** 53 | 54 | - \[Glimmer2\] Use the document service for glimmer [\#73](https://github.com/yapplabs/ember-wormhole/pull/73) ([chadhietala](https://github.com/chadhietala)) 55 | - Update ember-cli to 2.8.0 and associated dependencies and files. [\#72](https://github.com/yapplabs/ember-wormhole/pull/72) ([runspired](https://github.com/runspired)) 56 | - Test for dynamic content in Glimmer2 [\#68](https://github.com/yapplabs/ember-wormhole/pull/68) ([simonihmig](https://github.com/simonihmig)) 57 | 58 | ## [0.4.1](https://github.com/yapplabs/ember-wormhole/tree/0.4.1) (2016-09-12) 59 | [Full Changelog](https://github.com/yapplabs/ember-wormhole/compare/0.4.0...0.4.1) 60 | 61 | **Merged pull requests:** 62 | 63 | - Use `getDOM` util to get dom reference in glimmer2 [\#65](https://github.com/yapplabs/ember-wormhole/pull/65) ([bantic](https://github.com/bantic)) 64 | - \[DOC\] Add Ember version compatibility to README [\#63](https://github.com/yapplabs/ember-wormhole/pull/63) ([lukemelia](https://github.com/lukemelia)) 65 | - Use native doc.getElementById when available [\#61](https://github.com/yapplabs/ember-wormhole/pull/61) ([bantic](https://github.com/bantic)) 66 | - Test ember-alpha in CI. [\#60](https://github.com/yapplabs/ember-wormhole/pull/60) ([rwjblue](https://github.com/rwjblue)) 67 | 68 | ## [0.4.0](https://github.com/yapplabs/ember-wormhole/tree/0.4.0) (2016-06-08) 69 | [Full Changelog](https://github.com/yapplabs/ember-wormhole/compare/0.3.6...0.4.0) 70 | 71 | **Merged pull requests:** 72 | 73 | - Fix initializer arity deprecation [\#56](https://github.com/yapplabs/ember-wormhole/pull/56) ([bantic](https://github.com/bantic)) 74 | - Remove `needsBindAttr` from x-favicon component in tests [\#55](https://github.com/yapplabs/ember-wormhole/pull/55) ([bantic](https://github.com/bantic)) 75 | - Refactor to public APIs for head/tail [\#54](https://github.com/yapplabs/ember-wormhole/pull/54) ([mixonic](https://github.com/mixonic)) 76 | - Update ember-cli 1.13.7 to 2.5.1 [\#53](https://github.com/yapplabs/ember-wormhole/pull/53) ([mixonic](https://github.com/mixonic)) 77 | 78 | ## [0.3.6](https://github.com/yapplabs/ember-wormhole/tree/0.3.6) (2016-06-02) 79 | [Full Changelog](https://github.com/yapplabs/ember-wormhole/compare/0.3.5...0.3.6) 80 | 81 | **Merged pull requests:** 82 | 83 | - Avoid bind-attr in tests for newer Ember [\#51](https://github.com/yapplabs/ember-wormhole/pull/51) ([bantic](https://github.com/bantic)) 84 | - Update ember-code-snippet to ^1.3.0 [\#50](https://github.com/yapplabs/ember-wormhole/pull/50) ([bantic](https://github.com/bantic)) 85 | 86 | ## [0.3.5](https://github.com/yapplabs/ember-wormhole/tree/0.3.5) (2016-02-01) 87 | [Full Changelog](https://github.com/yapplabs/ember-wormhole/compare/0.3.4...0.3.5) 88 | 89 | **Merged pull requests:** 90 | 91 | - Upgrade to ember-cli 1.13.7 [\#32](https://github.com/yapplabs/ember-wormhole/pull/32) ([chrislopresto](https://github.com/chrislopresto)) 92 | - Update to Ember-CLI 1.13.1 [\#30](https://github.com/yapplabs/ember-wormhole/pull/30) ([chrislopresto](https://github.com/chrislopresto)) 93 | - Preserve focus [\#23](https://github.com/yapplabs/ember-wormhole/pull/23) ([adamesque](https://github.com/adamesque)) 94 | 95 | ## [0.3.4](https://github.com/yapplabs/ember-wormhole/tree/0.3.4) (2015-07-26) 96 | [Full Changelog](https://github.com/yapplabs/ember-wormhole/compare/0.3.3...0.3.4) 97 | 98 | **Merged pull requests:** 99 | 100 | - BUGFIX fix tests to support beta and canary [\#29](https://github.com/yapplabs/ember-wormhole/pull/29) ([krisselden](https://github.com/krisselden)) 101 | - Support extending via didInsert/willDestroy [\#26](https://github.com/yapplabs/ember-wormhole/pull/26) ([chrislopresto](https://github.com/chrislopresto)) 102 | 103 | ## [0.3.3](https://github.com/yapplabs/ember-wormhole/tree/0.3.3) (2015-06-24) 104 | [Full Changelog](https://github.com/yapplabs/ember-wormhole/compare/0.3.2...0.3.3) 105 | 106 | ## [0.3.2](https://github.com/yapplabs/ember-wormhole/tree/0.3.2) (2015-06-24) 107 | [Full Changelog](https://github.com/yapplabs/ember-wormhole/compare/0.3.1...0.3.2) 108 | 109 | **Merged pull requests:** 110 | 111 | - fix issue when firstNode is the same as lastNode [\#15](https://github.com/yapplabs/ember-wormhole/pull/15) ([piotrpalek](https://github.com/piotrpalek)) 112 | 113 | ## [0.3.1](https://github.com/yapplabs/ember-wormhole/tree/0.3.1) (2015-04-28) 114 | [Full Changelog](https://github.com/yapplabs/ember-wormhole/compare/0.2.1...0.3.1) 115 | 116 | **Merged pull requests:** 117 | 118 | - Add ability to render in place [\#8](https://github.com/yapplabs/ember-wormhole/pull/8) ([chrislopresto](https://github.com/chrislopresto)) 119 | 120 | ## [0.2.1](https://github.com/yapplabs/ember-wormhole/tree/0.2.1) (2015-04-17) 121 | [Full Changelog](https://github.com/yapplabs/ember-wormhole/compare/0.2.0...0.2.1) 122 | 123 | ## [0.2.0](https://github.com/yapplabs/ember-wormhole/tree/0.2.0) (2015-04-17) 124 | [Full Changelog](https://github.com/yapplabs/ember-wormhole/compare/0.1.0...0.2.0) 125 | 126 | **Merged pull requests:** 127 | 128 | - Use Public APIs [\#7](https://github.com/yapplabs/ember-wormhole/pull/7) ([chrislopresto](https://github.com/chrislopresto)) 129 | - Add tests for wormholing into the head of the Ember app [\#5](https://github.com/yapplabs/ember-wormhole/pull/5) ([tim-evans](https://github.com/tim-evans)) 130 | - Add comment about location of destination element [\#1](https://github.com/yapplabs/ember-wormhole/pull/1) ([knownasilya](https://github.com/knownasilya)) 131 | 132 | ## [0.1.0](https://github.com/yapplabs/ember-wormhole/tree/0.1.0) (2015-04-05) 133 | 134 | 135 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How To Contribute 2 | 3 | ## Installation 4 | 5 | * `git clone ` 6 | * `cd ember-wormhole` 7 | * `yarn install` 8 | 9 | ## Linting 10 | 11 | * `yarn lint:hbs` 12 | * `yarn lint:js` 13 | * `yarn lint:js --fix` 14 | 15 | ## Running tests 16 | 17 | * `ember test` – Runs the test suite on the current Ember version 18 | * `ember test --server` – Runs the test suite in "watch mode" 19 | * `ember try:each` – Runs the test suite against multiple Ember versions 20 | 21 | ## Running the dummy application 22 | 23 | * `ember serve` 24 | * Visit the dummy application at [http://localhost:4200](http://localhost:4200). 25 | 26 | For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/). 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ember Wormhole [![Build Status](https://travis-ci.org/yapplabs/ember-wormhole.svg?branch=master)](https://travis-ci.org/yapplabs/ember-wormhole) [![Ember Observer Score](http://emberobserver.com/badges/ember-wormhole.svg)](http://emberobserver.com/addons/ember-wormhole) 2 | 3 | This addon provides a component that allows for rendering a block 4 | to a DOM element somewhere else on the page. The component retains typical Ember 5 | context in terms of bound data and action handling. Ember Wormhole is 6 | compatible with [Ember FastBoot](http://www.ember-fastboot.com/) as of version 7 | 0.4.0, so long as the destination element is part of Ember's own templates. 8 | 9 | ## Live Demo 10 | 11 | View a live demo here: [http://yapplabs.github.io/ember-wormhole/](http://yapplabs.github.io/ember-wormhole/) 12 | 13 | The source code for the demo is available here: [https://github.com/yapplabs/ember-wormhole/tree/master/tests/dummy/app](https://github.com/yapplabs/ember-wormhole/tree/master/tests/dummy/app) 14 | 15 | ## But Why? 16 | 17 | This library is particularly useful for cases where you have UI that is the logical child of 18 | a component but needs to render as a top-level DOM element, such as a confirmation dialog. 19 | 20 | ## And How? 21 | 22 | This component tracks its element's child nodes. When inserted into the DOM, it appends 23 | its child nodes to a destination element elsewhere. When removed from the DOM, it 24 | removes its child nodes, so as not to orphan them on the other side of the wormhole. 25 | 26 | Nothing else changes -- data binding and action bubbling still flow according to 27 | the Ember component hierarchy. That includes usages of `yield`, so blocks provided 28 | to `ember-wormhole` simply appear in another part of the DOM. 29 | 30 | ## Show Me Some Code! 31 | 32 | We thought you'd never ask... 33 | 34 | Given the following DOM: 35 | 36 | ```html 37 | 38 | 39 | 40 |
41 |
42 |
43 | 44 |
45 | 46 | ``` 47 | 48 | and a template like this: 49 | 50 | ```hbs 51 | {{#ember-wormhole to="destination"}} 52 | Hello world! 53 | {{/ember-wormhole}} 54 | ``` 55 | 56 | Then "Hello world!" would be rendered inside the `destination` div. 57 | 58 | If the ember-wormhole is destroyed its far-off children are destroyed too. 59 | For example, given: 60 | 61 | ```hbs 62 | {{#if isWormholeEnabled}} 63 | {{#ember-wormhole to="destination"}} 64 | Hello world! 65 | {{/ember-wormhole}} 66 | {{/if}} 67 | ``` 68 | 69 | If `isWormholeEnabled` starts off true and becomes false, then the "Hello 70 | world!" text will be removed from the `destination` div. 71 | 72 | Similarly, if you use `ember-wormhole` in a route's template, it will 73 | render its children in the destination element when the route is entered 74 | and remove them when the route is exited. 75 | 76 | ## Can I Render In Place (i.e. Unwormhole)? 77 | 78 | Yes! Sometimes you feel like a wormhole. Sometimes you don't. Situations 79 | sometimes call for the same content to be rendered through the wormhole or in place. 80 | 81 | In this example, `renderInPlace` will override `to` and cause the wormhole content to be rendered in place. 82 | 83 | ```hbs 84 | {{#ember-wormhole to="destination" renderInPlace=true}} 85 | Hello world! 86 | {{/ember-wormhole}} 87 | ``` 88 | 89 | This technique is useful for: 90 | 91 | - Presenting typically-wormholed content within a styleguide 92 | - Toggling content back and forth through the wormhole 93 | - Parlor tricks 94 | 95 | ## What if if my element has no id? 96 | 97 | You can provide an element directly to the wormhole. For example: 98 | 99 | ```hbs 100 | {{#ember-wormhole destinationElement=someElement}} 101 | Hello world! 102 | {{/ember-wormhole}} 103 | ``` 104 | 105 | This usage may be appropriate when using wormhole with dynamic targets, 106 | such as rendering into all elements matching a selector. 107 | 108 | ## What Version of Ember is This Compatible With? 109 | 110 | This library is compatible with and tested against Ember 1.13 and higher. 111 | 112 | ### Important Note about using this library with Ember 2.10 113 | 114 | With latest ember-wormhole and ember@2.10, you need to have a stable root element inside the wormhole block. This is something that the Ember Core team will continue to iterate and work on, but for now the work around is fairly straightforward. 115 | 116 | Change: 117 | 118 | ```hbs 119 | {{#ember-wormhole to="worm"}} 120 | {{#if foo}} 121 | 122 | {{/if}} 123 |

Other content, whatever

124 | {{/ember-wormhole}} 125 | To: 126 | 127 | {{#ember-wormhole to="worm"}} 128 |
129 | {{#if foo}} 130 | 131 | {{/if}} 132 |

Other content, whatever

133 |
134 | {{/ember-wormhole}} 135 | ``` 136 | 137 | ## Ember's native in-element 138 | 139 | Since Ember 3.21 there is also a native `in-element` helper. This helper offer a bit less functionality than this addon, 140 | but may be enough for your use case! For more info see 141 | [the in-element API docs](https://api.emberjs.com/ember/3.21/classes/Ember.Templates.helpers/methods/in-element?anchor=in-element) 142 | and [the excellent article by Faith Or comparing ember-wormhole and in-element](https://www.linkedin.com/pulse/emberjs-using-in-element-helper-faith-or/) 143 | 144 | ## Development Setup 145 | 146 | ### Simple Installation 147 | To add the ember-wormhole add-on to an existing project, enter this command from the root of your EmberJS project: 148 | 149 | * `ember install ember-wormhole` 150 | 151 | ### Setting Up The Demo 152 | If you'd like to set up a new EmberJS application with the ember-wormhole sample application configured, follow these steps: 153 | 154 | * `git clone` this repository 155 | * `npm install` 156 | * `bower install` 157 | 158 | ### Running Tests 159 | 160 | * `ember try:testall` 161 | * `ember test` 162 | * `ember test --server` 163 | 164 | ### Running the dummy app 165 | 166 | * `ember server` 167 | * Visit your app at http://localhost:4200. 168 | 169 | For more information on using ember-cli, visit [http://www.ember-cli.com/](http://www.ember-cli.com/). 170 | 171 | ## Credits 172 | 173 | This addon was extracted from [ember-modal-dialog](http://github.com/yapplabs/ember-modal-dialog). 174 | Contributions from @stefanpenner, @krisselden, @chrislopresto, @lukemelia, @raycohen and 175 | others. [Yapp Labs](http://yapplabs.com) is an Ember.js consultancy based in NYC. 176 | -------------------------------------------------------------------------------- /addon/components/ember-wormhole.js: -------------------------------------------------------------------------------- 1 | import { alias } from '@ember/object/computed'; 2 | import Component from '@ember/component'; 3 | import { observer, computed } from '@ember/object'; 4 | import { schedule } from '@ember/runloop'; 5 | import layout from '../templates/components/ember-wormhole'; 6 | import { 7 | getActiveElement, 8 | findElementById, 9 | getDOM 10 | } from '../utils/dom'; 11 | 12 | export default Component.extend({ 13 | layout, 14 | 15 | /* 16 | * Attrs 17 | */ 18 | to: alias('destinationElementId'), 19 | destinationElementId: null, 20 | destinationElement: null, 21 | 22 | _destination: computed('destinationElement', 'destinationElementId', 'renderInPlace', function() { 23 | let renderInPlace = this.get('renderInPlace'); 24 | if (renderInPlace) { 25 | return this._element; 26 | } 27 | 28 | let destinationElement = this.get('destinationElement'); 29 | if (destinationElement) { 30 | return destinationElement; 31 | } 32 | let destinationElementId = this.get('destinationElementId'); 33 | if (destinationElementId) { 34 | return findElementById(this._dom, destinationElementId); 35 | } 36 | // no element found 37 | return null; 38 | }), 39 | 40 | renderInPlace: false, 41 | 42 | /* 43 | * Lifecycle 44 | */ 45 | init() { 46 | this._super(...arguments); 47 | 48 | this._dom = getDOM(this); 49 | 50 | // Create text nodes used for the head, tail 51 | this._wormholeHeadNode = this._dom.createTextNode(''); 52 | this._wormholeTailNode = this._dom.createTextNode(''); 53 | 54 | /* 55 | * didInsertElement does not fire in Fastboot, so we schedule this in 56 | * init to be run after render. Importantly, we want to run 57 | * appendToDestination after the child nodes have rendered. 58 | */ 59 | schedule('afterRender', () => { 60 | if (this.isDestroyed) { return; } 61 | this._element = this._wormholeHeadNode.parentNode; 62 | if (!this._element) { 63 | throw new Error('The head node of a wormhole must be attached to the DOM'); 64 | } 65 | this._appendToDestination(); 66 | }); 67 | }, 68 | 69 | willDestroyElement: function() { 70 | // not called in fastboot 71 | this._super(...arguments); 72 | let { _wormholeHeadNode, _wormholeTailNode } = this; 73 | schedule('render', () => { 74 | this._removeRange(_wormholeHeadNode, _wormholeTailNode); 75 | }); 76 | }, 77 | 78 | _destinationDidChange: observer('_destination', function() { 79 | var destinationElement = this.get('_destination'); 80 | if (destinationElement !== this._wormholeHeadNode.parentNode) { 81 | schedule('render', this, '_appendToDestination'); 82 | } 83 | }), 84 | 85 | _appendToDestination() { 86 | var destinationElement = this.get('_destination'); 87 | if (!destinationElement) { 88 | var destinationElementId = this.get('destinationElementId'); 89 | if (destinationElementId) { 90 | throw new Error(`ember-wormhole failed to render into '#${destinationElementId}' because the element is not in the DOM`); 91 | } 92 | throw new Error('ember-wormhole failed to render content because the destinationElementId was set to an undefined or falsy value.'); 93 | } 94 | 95 | let startingActiveElement = getActiveElement(); 96 | this._appendRange(destinationElement, this._wormholeHeadNode, this._wormholeTailNode); 97 | let resultingActiveElement = getActiveElement(); 98 | if (startingActiveElement && resultingActiveElement !== startingActiveElement) { 99 | startingActiveElement.focus(); 100 | } 101 | }, 102 | 103 | _appendRange(destinationElement, firstNode, lastNode) { 104 | while(firstNode) { 105 | destinationElement.insertBefore(firstNode, null); 106 | firstNode = firstNode !== lastNode ? lastNode.parentNode.firstChild : null; 107 | } 108 | }, 109 | 110 | _removeRange(firstNode, lastNode) { 111 | var node = lastNode; 112 | do { 113 | var next = node.previousSibling; 114 | if (node.parentNode) { 115 | node.parentNode.removeChild(node); 116 | if (node === firstNode) { 117 | break; 118 | } 119 | } 120 | node = next; 121 | } while (node); 122 | }, 123 | }); 124 | -------------------------------------------------------------------------------- /addon/templates/components/ember-wormhole.hbs: -------------------------------------------------------------------------------- 1 | {{unbound this._wormholeHeadNode ~}} 2 | {{yield ~}} 3 | {{unbound this._wormholeTailNode ~}} -------------------------------------------------------------------------------- /addon/utils/dom.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Implement some helpers methods for interacting with the DOM, 3 | * be it Fastboot's SimpleDOM or the browser's version. 4 | */ 5 | 6 | import { getOwner } from '@ember/application'; 7 | 8 | export function getActiveElement() { 9 | if (typeof document === 'undefined') { 10 | return null; 11 | } else { 12 | return document.activeElement; 13 | } 14 | } 15 | 16 | function childNodesOfElement(element) { 17 | let children = []; 18 | let child = element.firstChild; 19 | while (child) { 20 | children.push(child); 21 | child = child.nextSibling; 22 | } 23 | return children; 24 | } 25 | 26 | export function findElementById(doc, id) { 27 | if (doc.getElementById) { 28 | return doc.getElementById(id); 29 | } 30 | 31 | let nodes = childNodesOfElement(doc); 32 | let node; 33 | 34 | while (nodes.length) { 35 | node = nodes.shift(); 36 | 37 | if (node.getAttribute && node.getAttribute('id') === id) { 38 | return node; 39 | } 40 | 41 | nodes = childNodesOfElement(node).concat(nodes); 42 | } 43 | } 44 | 45 | // Private Ember API usage. Get the dom implementation used by the current 46 | // renderer, be it native browser DOM or Fastboot SimpleDOM 47 | export function getDOM(context) { 48 | let { renderer } = context; 49 | if (!renderer._dom) { // pre glimmer2 50 | let container = getOwner ? getOwner(context) : context.container; 51 | let documentService = container.lookup('service:-document'); 52 | 53 | if (documentService) { return documentService; } 54 | 55 | renderer = container.lookup('renderer:-dom'); 56 | } 57 | 58 | if (renderer._dom && renderer._dom.document) { // pre Ember 2.6 59 | return renderer._dom.document; 60 | } else { 61 | throw new Error('ember-wormhole could not get DOM'); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/components/ember-wormhole.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-wormhole/components/ember-wormhole'; 2 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getChannelURL = require('ember-source-channel-url'); 4 | 5 | module.exports = async function() { 6 | return { 7 | useYarn: true, 8 | scenarios: [ 9 | { 10 | name: 'ember-lts-3.16', 11 | npm: { 12 | devDependencies: { 13 | 'ember-source': '~3.16.0' 14 | } 15 | } 16 | }, 17 | { 18 | name: 'ember-lts-3.20', 19 | npm: { 20 | devDependencies: { 21 | 'ember-source': '~3.20.5' 22 | } 23 | } 24 | }, 25 | { 26 | name: 'ember-release', 27 | npm: { 28 | devDependencies: { 29 | 'ember-source': await getChannelURL('release') 30 | } 31 | } 32 | }, 33 | { 34 | name: 'ember-beta', 35 | npm: { 36 | devDependencies: { 37 | 'ember-source': await getChannelURL('beta') 38 | } 39 | } 40 | }, 41 | { 42 | name: 'ember-canary', 43 | npm: { 44 | devDependencies: { 45 | 'ember-source': await getChannelURL('canary') 46 | } 47 | } 48 | }, 49 | { 50 | name: 'ember-default-with-jquery', 51 | env: { 52 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 53 | 'jquery-integration': true 54 | }) 55 | }, 56 | npm: { 57 | devDependencies: { 58 | '@ember/jquery': '^1.1.0' 59 | } 60 | } 61 | }, 62 | { 63 | name: 'ember-classic', 64 | env: { 65 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 66 | 'application-template-wrapper': true, 67 | 'default-async-observers': false, 68 | 'template-only-glimmer-components': false 69 | }) 70 | }, 71 | npm: { 72 | ember: { 73 | edition: 'classic' 74 | } 75 | } 76 | } 77 | ] 78 | }; 79 | }; 80 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | name: require('./package').name 5 | }; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-wormhole", 3 | "version": "0.6.0", 4 | "description": "Render a child view somewhere else in the DOM.", 5 | "keywords": [ 6 | "ember-addon" 7 | ], 8 | "repository": "https://github.com/yapplabs/ember-wormhole.git", 9 | "license": "MIT", 10 | "author": "Yapp Labs", 11 | "directories": { 12 | "doc": "doc", 13 | "test": "tests" 14 | }, 15 | "scripts": { 16 | "build": "ember build --environment=production", 17 | "lint": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*", 18 | "lint:hbs": "ember-template-lint .", 19 | "lint:js": "eslint .", 20 | "start": "ember serve", 21 | "test": "npm-run-all lint:* test:*", 22 | "test:ember": "ember test", 23 | "test:ember-compatibility": "ember try:each" 24 | }, 25 | "dependencies": { 26 | "ember-cli-babel": "^7.22.1", 27 | "ember-cli-htmlbars": "^5.3.1" 28 | }, 29 | "devDependencies": { 30 | "@ember/optional-features": "^2.0.0", 31 | "@glimmer/component": "^1.0.2", 32 | "@glimmer/tracking": "^1.0.2", 33 | "babel-eslint": "^10.1.0", 34 | "broccoli-asset-rev": "^3.0.0", 35 | "ember-auto-import": "^1.6.0", 36 | "ember-cli": "^3.22.0", 37 | "ember-cli-dependency-checker": "^3.2.0", 38 | "ember-cli-fastboot": "^2.2.3", 39 | "ember-cli-fastboot-testing": "^0.4.0", 40 | "ember-cli-github-pages": "^0.2.2", 41 | "ember-cli-inject-live-reload": "^2.0.2", 42 | "ember-cli-release": "^1.0.0-beta.2", 43 | "ember-cli-sri": "^2.1.1", 44 | "ember-cli-terser": "^4.0.0", 45 | "ember-code-snippet": "^3.0.0", 46 | "ember-disable-prototype-extensions": "^1.1.3", 47 | "ember-export-application-global": "^2.0.1", 48 | "ember-load-initializers": "^2.1.1", 49 | "ember-maybe-import-regenerator": "^0.1.6", 50 | "ember-qunit": "^4.6.0", 51 | "ember-resolver": "^8.0.2", 52 | "ember-source": "~3.22.0", 53 | "ember-source-channel-url": "^3.0.0", 54 | "ember-template-lint": "^2.14.0", 55 | "ember-try": "^1.4.0", 56 | "eslint": "^7.11.0", 57 | "eslint-plugin-ember": "^9.3.0", 58 | "eslint-plugin-node": "^11.1.0", 59 | "loader.js": "^4.7.0", 60 | "npm-run-all": "^4.1.5", 61 | "qunit-dom": "^1.5.0" 62 | }, 63 | "engines": { 64 | "node": "10.* || >= 12" 65 | }, 66 | "ember": { 67 | "edition": "octane" 68 | }, 69 | "ember-addon": { 70 | "configPath": "tests/dummy/config", 71 | "demoURL": "http://yapplabs.github.io/ember-wormhole/" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | test_page: 'tests/index.html?hidepassed', 5 | disable_watching: true, 6 | launch_in_ci: [ 7 | 'Chrome' 8 | ], 9 | launch_in_dev: [ 10 | 'Chrome' 11 | ], 12 | browser_start_timeout: 120, 13 | browser_args: { 14 | Chrome: { 15 | ci: [ 16 | // --no-sandbox is needed when running Chrome inside a container 17 | process.env.CI ? '--no-sandbox' : null, 18 | '--headless', 19 | '--disable-dev-shm-usage', 20 | '--disable-software-rasterizer', 21 | '--mute-audio', 22 | '--remote-debugging-port=0', 23 | '--window-size=1440,900', 24 | // --no-sandbox is needed when running Chrome inside a container 25 | process.env.TRAVIS ? '--no-sandbox' : null, 26 | ].filter(Boolean) 27 | } 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /tests/acceptance/wormhole-test.js: -------------------------------------------------------------------------------- 1 | import { set } from '@ember/object'; 2 | import QUnit, { module, test } from 'qunit'; 3 | import { setupApplicationTest } from 'ember-qunit'; 4 | import { click, fillIn, currentRouteName, visit, settled, setupOnerror } from '@ember/test-helpers'; 5 | import { getData } from 'dummy/utils/data'; 6 | 7 | function query(sel) { 8 | let match = /:contains\(([^)]+)\)/.exec(sel); 9 | let contains = null; 10 | if (match !== null) { 11 | contains = match[1]; 12 | sel = sel.slice(0, match.index); 13 | } 14 | 15 | let elems = Array.from(document.querySelectorAll(sel)); 16 | if (contains !== null) { 17 | elems = elems.filter((el) => el.textContent.includes(contains)); 18 | } 19 | return elems; 20 | } 21 | 22 | QUnit.assert.contentIn = function(sidebarId, content) { 23 | content = content || 'h1'; 24 | this.equal(query(`#${sidebarId} ${content}`).length, 1, `content is visible in sidebar #${sidebarId}`); 25 | }; 26 | 27 | QUnit.assert.contentNotIn = function(sidebarId, content) { 28 | content = content || 'h1'; 29 | this.equal(query(`#${sidebarId} ${content}`).length, 0, `content is not visible in sidebar #${sidebarId}`); 30 | }; 31 | 32 | module('Acceptance: Wormhole', function(hooks) { 33 | setupApplicationTest(hooks); 34 | 35 | test('modal example', async function(assert) { 36 | await visit('/'); 37 | assert.equal(currentRouteName(), 'index'); 38 | await click(query('button:contains(Toggle Modal)')[0]); 39 | assert.equal(query('#modals .overlay').length, 1, 'overlay is visible'); 40 | assert.equal(query('#modals .dialog').length, 1, 'dialog is visible'); 41 | await click('#modals .overlay'); 42 | assert.equal(query('#modals .overlay').length, 0, 'overlay is not visible'); 43 | assert.equal(query('#modals .dialog').length, 0, 'dialog is not visible'); 44 | await fillIn('.username', 'coco'); 45 | await click(query('button:contains(Toggle Modal)')[0]); 46 | assert.equal(query('#modals .dialog p:contains(coco)').length, 1, 'up-to-date username is shown in dialog'); 47 | }); 48 | 49 | test('sidebar example', async function(assert) { 50 | let sidebarWormhole; 51 | let header1, header2; 52 | let sidebarFirstNode1, sidebarFirstNode2; 53 | 54 | await visit('/'); 55 | assert.equal(currentRouteName(), 'index'); 56 | await click(query('button:contains(Toggle Sidebar Content)')[0]); 57 | sidebarWormhole = getData(document.getElementById('sidebarWormhole')); 58 | sidebarFirstNode1 = sidebarWormhole._wormholeHeadNode; 59 | header1 = query('#sidebar h1'); 60 | assert.contentIn('sidebar'); 61 | await fillIn('.first-name', 'Ray'); 62 | await fillIn('.last-name', 'Cohen'); 63 | assert.contentIn('sidebar', 'p:contains(Ray Cohen)'); 64 | await click(query('#sidebar button:contains(Switch)')[0]); 65 | sidebarFirstNode2 = sidebarWormhole._wormholeHeadNode; 66 | header2 = query('#othersidebar h1'); 67 | assert.equal(header1.textContent, header2.textContent, 'same header text'); 68 | assert.ok(header1[0] === header2[0], 'same header elements'); // appended elsewhere 69 | assert.ok(sidebarFirstNode1 === sidebarFirstNode2, 'different first nodes'); // appended elsewhere 70 | assert.contentNotIn('sidebar'); 71 | assert.contentIn('othersidebar'); 72 | await click(query('#othersidebar button:contains(Switch)')[0]); 73 | assert.contentIn('sidebar'); 74 | assert.contentNotIn('othersidebar'); 75 | await click(query('#sidebar button:contains(Hide)')[0]); 76 | assert.contentNotIn('sidebar'); 77 | assert.contentNotIn('othersidebar'); 78 | }); 79 | 80 | test('sidebar example in place', async function(assert) { 81 | await visit('/'); 82 | await click(query('button:contains(Toggle Sidebar Content)')[0]); 83 | assert.contentIn('sidebar'); 84 | assert.contentNotIn('othersidebar'); 85 | assert.contentNotIn('example-sidebar'); 86 | await click(query('button:contains(Toggle In Place)')[0]); 87 | assert.contentNotIn('sidebar'); 88 | assert.contentNotIn('othersidebar'); 89 | assert.contentIn('example-sidebar'); 90 | await click(query('button:contains(Switch Sidebars From Without)')[0]); 91 | assert.contentNotIn('sidebar'); 92 | assert.contentNotIn('othersidebar'); 93 | assert.contentIn('example-sidebar'); 94 | await click(query('button:contains(Toggle In Place)')[0]); 95 | assert.contentNotIn('sidebar'); 96 | assert.contentIn('othersidebar'); 97 | assert.contentNotIn('example-sidebar'); 98 | await click(query('button:contains(Hide)')[0]); 99 | assert.contentNotIn('sidebar'); 100 | assert.contentNotIn('othersidebar'); 101 | assert.contentNotIn('example-sidebar'); 102 | }); 103 | 104 | test('survives rerender', async function(assert) { 105 | let sidebarWormhole; 106 | let header1, header2; 107 | 108 | await visit('/'); 109 | assert.equal(currentRouteName(), 'index'); 110 | 111 | await click(query('button:contains(Toggle Sidebar Content)')[0]); 112 | sidebarWormhole = getData(document.getElementById('sidebarWormhole')); 113 | header1 = query('#sidebar h1'); 114 | assert.contentIn('sidebar'); 115 | 116 | await fillIn('.first-name', 'Ringo'); 117 | await fillIn('.last-name', 'Starr'); 118 | assert.contentIn('sidebar', 'p:contains(Ringo Starr)'); 119 | sidebarWormhole.rerender(); 120 | header2 = query('#sidebar h1'); 121 | assert.contentIn('sidebar', 'p:contains(Ringo Starr)'); 122 | assert.equal(header1.textContent, header2.textContent, 'same header text'); 123 | }); 124 | 125 | test('throws if destination element not in DOM', async function(assert) { 126 | await visit('/'); 127 | 128 | let lastError; 129 | setupOnerror((error) => lastError = error); 130 | 131 | let sidebar = document.getElementById('sidebar'); 132 | sidebar.parentNode.removeChild(sidebar); 133 | 134 | await click(query('button:contains(Toggle Sidebar Content)')[0]); 135 | 136 | assert.equal(lastError && lastError.message, "ember-wormhole failed to render into '#sidebar' because the element is not in the DOM"); 137 | }); 138 | 139 | test('throws if destination element id falsy', async function(assert) { 140 | await visit('/'); 141 | 142 | let lastError; 143 | setupOnerror((error) => lastError = error); 144 | 145 | this.owner.lookup('controller:index').set('sidebarId', null); 146 | await click(query('button:contains(Toggle Sidebar Content)')[0]); 147 | 148 | assert.equal(lastError && lastError.message, 'ember-wormhole failed to render content because the destinationElementId was set to an undefined or falsy value.'); 149 | }); 150 | 151 | test('preserves focus', async function(assert) { 152 | let sidebarWormhole; 153 | let focused; 154 | await visit('/'); 155 | assert.equal(currentRouteName(), 'index'); 156 | await click(query('button:contains(Toggle Sidebar Content)')[0]); 157 | sidebarWormhole = getData(query('#sidebarWormhole')[0]); 158 | assert.contentIn('sidebar'); 159 | assert.contentNotIn('othersidebar'); 160 | query('button:contains(Hide Sidebar Content)')[0].focus(); 161 | focused = document.activeElement; 162 | set(sidebarWormhole, 'to', 'othersidebar'); 163 | await settled(); 164 | assert.contentNotIn('sidebar'); 165 | assert.contentIn('othersidebar'); 166 | assert.equal(document.activeElement, focused); 167 | }); 168 | 169 | test('favicon example', async function(assert) { 170 | await visit('/'); 171 | let favicon = query('link[rel="icon"]')[0]; 172 | assert.equal(favicon.getAttribute('href'), 'http://emberjs.com/images/favicon.png'); 173 | 174 | await fillIn('.favicon', 'http://handlebarsjs.com/images/favicon.png'); 175 | let favicon2 = query('link[rel="icon"]')[0]; 176 | assert.equal(favicon2.getAttribute('href'), 'http://handlebarsjs.com/images/favicon.png'); 177 | }); 178 | 179 | test('document-title example', async function(assert) { 180 | await visit('/'); 181 | assert.equal(document.title, 'ember-wormhole'); 182 | 183 | await click('#toggle-title'); 184 | assert.equal(document.title, 'ember-wormhole Testing'); 185 | 186 | await click('#toggle-title'); 187 | assert.equal(document.title, 'ember-wormhole'); 188 | }); 189 | }); 190 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import Resolver from 'ember-resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from 'dummy/config/environment'; 5 | 6 | export default class App extends Application { 7 | modulePrefix = config.modulePrefix; 8 | podModulePrefix = config.podModulePrefix; 9 | Resolver = Resolver; 10 | } 11 | 12 | loadInitializers(App, config.modulePrefix); 13 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-wormhole/4966be5d369d0e0ca6bfc50bba6b7d8a19da0c48/tests/dummy/app/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/components/current-user.js: -------------------------------------------------------------------------------- 1 | import Component from '@ember/component'; 2 | import { A } from '@ember/array'; 3 | import EmberObject, { computed } from '@ember/object'; 4 | 5 | var User = EmberObject.extend({ 6 | username: null, 7 | firstName: null, 8 | lastName: null, 9 | fullName: computed('firstName', 'lastName', function(){ 10 | return A([this.get('firstName'), this.get('lastName')]).compact().join(' '); 11 | }) 12 | }); 13 | 14 | export default Component.extend({ 15 | user: User.create({ 16 | username: 'krisselden', 17 | firstName: 'Kris', 18 | lastName: 'Selden' 19 | }) 20 | }); 21 | -------------------------------------------------------------------------------- /tests/dummy/app/components/document-title.js: -------------------------------------------------------------------------------- 1 | import { computed } from '@ember/object'; 2 | import { A } from '@ember/array'; 3 | import Wormhole from 'ember-wormhole/components/ember-wormhole'; 4 | 5 | var titles = A([]); 6 | export default Wormhole.extend({ 7 | init: function () { 8 | this._super(); 9 | 10 | if (titles.length === 0) { 11 | this._dom.title = ''; 12 | } 13 | titles.push(this); 14 | }, 15 | 16 | destinationElement: computed(function () { 17 | let head = this._dom.head; 18 | let node = head.firstChild; 19 | while (node !== null) { 20 | if (node.nodeType === 1 && node.tagName === 'TITLE') { 21 | return node; 22 | } 23 | node = node.nextSibling; 24 | } 25 | node = this._dom.createElement('title'); 26 | head.appendChild(node); 27 | return node; 28 | }), 29 | 30 | willDestroyElement: function () { 31 | titles.removeObject(this); 32 | this._super.apply(this, arguments); 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /tests/dummy/app/components/ember-wormhole.js: -------------------------------------------------------------------------------- 1 | import { on } from '@ember/object/evented'; 2 | import EmberWormhole from 'ember-wormhole/components/ember-wormhole'; 3 | import { setData, removeData } from 'dummy/utils/data'; 4 | 5 | export default EmberWormhole.extend({ 6 | _storeSelf: on('didInsertElement', function () { 7 | setData(this.element, this); 8 | }), 9 | 10 | _removeSelf: on('willDestroyElement', function () { 11 | removeData(this.element); 12 | }) 13 | }); 14 | -------------------------------------------------------------------------------- /tests/dummy/app/components/x-favicon.js: -------------------------------------------------------------------------------- 1 | import { computed } from '@ember/object'; 2 | import Wormhole from 'ember-wormhole/components/ember-wormhole'; 3 | import layout from '../templates/components/x-favicon'; 4 | 5 | export default Wormhole.extend({ 6 | layout, 7 | destinationElement: computed.reads('_dom.head') 8 | }); 9 | -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-wormhole/4966be5d369d0e0ca6bfc50bba6b7d8a19da0c48/tests/dummy/app/controllers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/index.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | import { set } from '@ember/object'; 3 | 4 | export default Controller.extend({ 5 | isShowingModal: false, 6 | isShowingSidebarContent: false, 7 | sidebarId: 'sidebar', 8 | isInPlace: false, 9 | isTestingDocumentTitle: false, 10 | favicon: "http://emberjs.com/images/favicon.png", 11 | isShowingOverlay: true, 12 | actions: { 13 | toggleModal() { 14 | this.toggleProperty('isShowingModal'); 15 | }, 16 | toggleSidebarContent() { 17 | this.toggleProperty('isShowingSidebarContent'); 18 | }, 19 | switchSidebars() { 20 | var otherSidebarId = this.sidebarId === 'sidebar' ? 'othersidebar' : 'sidebar'; 21 | set(this, 'sidebarId', otherSidebarId); 22 | }, 23 | toggleInPlace() { 24 | this.toggleProperty('isInPlace'); 25 | }, 26 | toggleTitle() { 27 | this.toggleProperty('isTestingDocumentTitle'); 28 | }, 29 | toggleOverlay() { 30 | this.toggleProperty('isShowingOverlay'); 31 | } 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-wormhole/4966be5d369d0e0ca6bfc50bba6b7d8a19da0c48/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/initializers/add-modals-element.js: -------------------------------------------------------------------------------- 1 | function initialize(){ 2 | if (typeof document !== 'undefined') { 3 | // In Ember 2.x, initialize is called only with `application` instead of `(container, application)` 4 | // See http://emberjs.com/deprecations/v2.x/#toc_initializer-arity 5 | var application = arguments[1] || arguments[0]; 6 | var rootEl = document.querySelector(application.rootElement); 7 | var modalContainerEl = document.createElement('div'); 8 | var modalContainerElId = 'modals'; 9 | modalContainerEl.id = modalContainerElId; 10 | rootEl.appendChild(modalContainerEl); 11 | } 12 | } 13 | 14 | export default { 15 | name: 'add-modals-element', 16 | initialize: initialize 17 | }; 18 | -------------------------------------------------------------------------------- /tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-wormhole/4966be5d369d0e0ca6bfc50bba6b7d8a19da0c48/tests/dummy/app/models/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@ember/routing/router'; 2 | import config from 'dummy/config/environment'; 3 | 4 | export default class Router extends EmberRouter { 5 | location = config.locationType; 6 | rootURL = config.rootURL; 7 | } 8 | 9 | Router.map(function() { 10 | this.route('wormhole'); 11 | this.route('inplace'); 12 | }); 13 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-wormhole/4966be5d369d0e0ca6bfc50bba6b7d8a19da0c48/tests/dummy/app/routes/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Trebuchet', 'Helvetica', sans-serif; 3 | font-size: 14px; 4 | line-height: 1.4em; 5 | } 6 | .sidebar { 7 | margin-left:40px; 8 | padding:20px; 9 | min-height: 300px; 10 | border:1px solid #999; 11 | width:200px; 12 | position: fixed; 13 | right: 0; 14 | } 15 | #othersidebar { 16 | top: 400px; 17 | } 18 | .example { 19 | margin-right: 240px; 20 | } 21 | .example { 22 | border: 1px dotted #ccc; 23 | padding: 10px 20px; 24 | max-width:675px; 25 | margin-top: 50px; 26 | } 27 | button { 28 | display: inline-block; 29 | margin:10px; 30 | } 31 | label { 32 | display:block; 33 | min-height:1.5rem; 34 | } 35 | .overlay { 36 | height: 100vh; 37 | left: 0; 38 | opacity: 0; 39 | position: fixed; 40 | right: 0; 41 | top: 0; 42 | z-index: 50; 43 | opacity: 0.77; 44 | filter: alpha(opacity=15); 45 | background-color: #808080; 46 | } 47 | .dialog { 48 | position: fixed; 49 | z-index: 50; 50 | background: white; 51 | width: 500px; 52 | height: 300px; 53 | border: 4px solid #cc8; 54 | border-radius: 10px; 55 | box-shadow: 0 0 10px #222; 56 | padding: 10px; 57 | left:50%; 58 | top:50%; 59 | margin-left: -250px; 60 | margin-top: -150px; 61 | } 62 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 | {{outlet}} -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/current-user.hbs: -------------------------------------------------------------------------------- 1 | {{yield user}} 2 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/x-favicon.hbs: -------------------------------------------------------------------------------- 1 | {{this._wormholeHeadNode ~}} 2 | 3 | {{this._wormholeTailNode ~}} -------------------------------------------------------------------------------- /tests/dummy/app/templates/index.hbs: -------------------------------------------------------------------------------- 1 | {{x-favicon href=favicon}} 2 | 3 | {{#if isTestingDocumentTitle}} 4 | {{#document-title}} Testing{{/document-title}} 5 | {{/if}} 6 | {{#document-title}}ember-wormhole{{/document-title}} 7 | 8 |

ember-wormhole

9 | 10 | 11 | 12 | 13 |

14 | Welcome. This ember-cli addon provides a component that allows for rendering 15 | a block in a typical Ember context in terms of bound data and action handling, 16 | but attached to a DOM element somewhere else on the page. 17 |

18 | 19 |

20 | The example app you are looking at includes an intializer which adds a div 21 | to the body element of this document with an ID of modals. 22 | It also has a sidebar with an element with an ID of sidebar. 23 | Finally, there is a current-user component, which yields a 24 | user object and is used to demonstrate bound values across 25 | the wormhole boundary. 26 |

27 | 28 |

29 | In addition to the visible wormholes on screen, there are components 30 | that inject Ember into the head of the page. 31 |

32 | 33 | 34 | 37 | 38 |
39 |

Modal Example

40 | {{!-- BEGIN-SNIPPET modal --}} 41 | 42 | {{#current-user as |user|}} 43 | 46 | {{#if isShowingModal}} 47 | {{#ember-wormhole to='modals'}} 48 | {{#if isShowingOverlay}} 49 |
50 | {{/if}} 51 |
52 |

Hi, I'm a simple modal dialog

53 |

Here we have some content which is bound from the context 54 | where the wormhole component was used: "{{user.username}}"

55 | 56 | 57 |
58 | {{/ember-wormhole}} 59 | {{/if}} 60 | {{/current-user}} 61 | {{!-- END-SNIPPET --}} 62 |
63 | 64 |
65 |

Sidebar Example

66 | {{!-- BEGIN-SNIPPET sidebar --}} 67 | {{#current-user as |user|}} 68 | 69 | 70 | 71 | 74 | 77 | {{#if isShowingSidebarContent}} 78 | {{#ember-wormhole to=sidebarId id='sidebarWormhole' renderInPlace=isInPlace}} 79 |

Epic sidebar action

80 |

Here we have some content which is bound from the context 81 | where the wormhole component was used: "{{user.fullName}}"

82 | 83 | 84 | {{/ember-wormhole}} 85 | {{/if}} 86 | {{/current-user}} 87 | {{!-- END-SNIPPET --}} 88 |
-------------------------------------------------------------------------------- /tests/dummy/app/templates/inplace.hbs: -------------------------------------------------------------------------------- 1 |

inplace

2 | 3 |
4 | 5 |
6 | {{#ember-wormhole to="destination" renderInPlace=true}} 7 | Hello world! 8 | {{/ember-wormhole}} 9 |
10 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/wormhole.hbs: -------------------------------------------------------------------------------- 1 |

wormhole

2 | 3 |
4 | 5 |
6 | {{#ember-wormhole to="destination"}} 7 | Hello world! 8 | {{/ember-wormhole}} 9 |
10 | -------------------------------------------------------------------------------- /tests/dummy/app/utils/data.js: -------------------------------------------------------------------------------- 1 | const map = new WeakMap(); 2 | 3 | export function setData(el, value) { 4 | map.set(el, value); 5 | } 6 | 7 | export function getData(el) { 8 | return map.get(el); 9 | } 10 | 11 | export function removeData(el) { 12 | map.delete(el); 13 | } 14 | -------------------------------------------------------------------------------- /tests/dummy/config/ember-cli-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "packages": [ 4 | { 5 | "name": "ember-cli", 6 | "version": "3.22.0", 7 | "blueprints": [ 8 | { 9 | "name": "addon", 10 | "outputRepo": "https://github.com/ember-cli/ember-addon-output", 11 | "codemodsSource": "ember-addon-codemods-manifest@1", 12 | "isBaseBlueprint": true, 13 | "options": [ 14 | "--yarn", 15 | "--no-welcome" 16 | ] 17 | } 18 | ] 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /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. EMBER_NATIVE_DECORATOR_SUPPORT: 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 === 'production') { 27 | ENV.rootURL = '/ember-wormhole'; // for gh-pages live demo 28 | } 29 | 30 | if (environment === 'development') { 31 | // ENV.APP.LOG_RESOLVER = true; 32 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 33 | // ENV.APP.LOG_TRANSITIONS = true; 34 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 35 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 36 | } 37 | 38 | if (environment === 'test') { 39 | // Testem prefers this... 40 | ENV.locationType = 'none'; 41 | 42 | // keep test console output quieter 43 | ENV.APP.LOG_ACTIVE_GENERATION = false; 44 | ENV.APP.LOG_VIEW_LOOKUPS = false; 45 | 46 | ENV.APP.rootElement = '#ember-testing'; 47 | ENV.APP.autoboot = false; 48 | } 49 | 50 | if (environment === 'production') { 51 | // here you can enable a production-specific feature 52 | } 53 | 54 | return ENV; 55 | }; 56 | -------------------------------------------------------------------------------- /tests/dummy/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "application-template-wrapper": false, 3 | "default-async-observers": true, 4 | "jquery-integration": false, 5 | "template-only-glimmer-components": true 6 | } 7 | -------------------------------------------------------------------------------- /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 = Boolean(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/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /tests/fastboot/inplace-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setup, visit, /* mockServer */ } from 'ember-cli-fastboot-testing/test-support'; 3 | 4 | module('FastBoot | inplace', function(hooks) { 5 | setup(hooks); 6 | 7 | test('it renders a page...', async function(assert) { 8 | await visit('/inplace'); 9 | 10 | assert.dom('#origin').hasText('Hello world!'); 11 | assert.dom('#destination').hasNoText(); 12 | }); 13 | 14 | }); 15 | -------------------------------------------------------------------------------- /tests/fastboot/wormhole-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setup, visit, /* mockServer */ } from 'ember-cli-fastboot-testing/test-support'; 3 | 4 | module('FastBoot | wormhole', function(hooks) { 5 | setup(hooks); 6 | 7 | test('it renders a page...', async function(assert) { 8 | await visit('/wormhole'); 9 | 10 | assert.dom('#destination').hasText('Hello world!'); 11 | assert.dom('#origin').hasNoText(); 12 | }); 13 | 14 | }); 15 | -------------------------------------------------------------------------------- /tests/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-wormhole/4966be5d369d0e0ca6bfc50bba6b7d8a19da0c48/tests/helpers/.gitkeep -------------------------------------------------------------------------------- /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/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/integration/components/ember-wormhole-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupRenderingTest } from 'ember-qunit'; 3 | import { render, settled } from '@ember/test-helpers'; 4 | import hbs from 'htmlbars-inline-precompile'; 5 | 6 | module('Integration | Component | ember wormhole', function(hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('it renders', async function(assert) { 10 | // Set any properties with this.set('myProperty', 'value'); 11 | // Handle any actions with this.on('myAction', function(val) { ... }); 12 | 13 | // Template block usage: 14 | await render(hbs` 15 |
16 | {{#ember-wormhole to="wormhole"}} 17 | template block text 18 | {{/ember-wormhole}} 19 | `); 20 | 21 | assert.equal(document.getElementById('wormhole').textContent.trim(), 'template block text'); 22 | }); 23 | 24 | test('if `renderInPlace` is truthy, the given `destinationElement` is ignored', async function(assert) { 25 | this.renderEnabled = false; 26 | this.renderInPlace = true; 27 | 28 | await render(hbs` 29 |
30 | {{#if renderEnabled}} 31 | {{#ember-wormhole renderInPlace=renderInPlace destinationElement=destinationElement}} 32 | template block text 33 | {{/ember-wormhole}} 34 | {{/if}} 35 | `); 36 | 37 | this.set('destinationElement', document.querySelector('#wormhole-destination-element')); 38 | this.set('renderEnabled', true); 39 | 40 | await settled(); 41 | 42 | let content = document.querySelector('#wormhole-content'); 43 | assert.notEqual(content.parentElement.id, 'wormhole-destination-element'); 44 | 45 | this.set('renderInPlace', false); 46 | await settled(); 47 | 48 | assert.equal(content.parentElement.id, 'wormhole-destination-element'); 49 | }); 50 | 51 | test('can switch `renderInPlace` with `destinationElementId`', async function(assert) { 52 | this.renderInPlace = true; 53 | 54 | await render(hbs` 55 |
56 | {{#ember-wormhole renderInPlace=renderInPlace destinationElementId="wormhole-destination-element"}} 57 | template block text 58 | {{/ember-wormhole}} 59 | `); 60 | 61 | let content = document.querySelector('#wormhole-content'); 62 | assert.notEqual(content.parentElement.id, 'wormhole-destination-element'); 63 | 64 | this.set('renderInPlace', false); 65 | 66 | await settled(); 67 | 68 | assert.equal(content.parentElement.id, 'wormhole-destination-element'); 69 | 70 | // switch back 71 | this.set('renderInPlace', true); 72 | 73 | await settled(); 74 | 75 | assert.notEqual(content.parentElement.id, 'wormhole-destination-element'); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /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/yapplabs/ember-wormhole/4966be5d369d0e0ca6bfc50bba6b7d8a19da0c48/tests/unit/.gitkeep -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-wormhole/4966be5d369d0e0ca6bfc50bba6b7d8a19da0c48/vendor/.gitkeep --------------------------------------------------------------------------------