diff --git a/.travis.yml b/.travis.yml index 8197d31..310b7cf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,17 +9,6 @@ cache: directories: - node_modules -env: - - EMBER_TRY_SCENARIO=default - - EMBER_TRY_SCENARIO=ember-release - - EMBER_TRY_SCENARIO=ember-beta - - EMBER_TRY_SCENARIO=ember-canary - -matrix: - fast_finish: true - allow_failures: - - env: EMBER_TRY_SCENARIO=ember-canary - before_install: - export PATH=/usr/local/phantomjs-2.0.0/bin:$PATH - "npm config set spin false" @@ -31,4 +20,4 @@ install: - bower install script: - - ember try $EMBER_TRY_SCENARIO test + - ember try:testall diff --git a/README.md b/README.md index 4ddd137..cc1c013 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,25 @@ if you have a model with a `status` field with an integer, you can do this: {{/x-select}} ``` +### Contextual Components + +As of version 2.1.0, `emberx-select` takes advantage of Ember's +[contextual components](http://emberjs.com/blog/2016/01/15/ember-2-3-released.html#toc_contextual-components) +feature. Using contextual components allows `emberx-select` to skip some +potentially expensive DOM traversals. This feature works with **Ember +2.3.0 and above!** If you're using such a version, we highly recommend +you use it: + +```handlebars +{{#x-select value=model.status as |xs|}} + {{#xs.option value=1}}Active{{/xs.option}} + {{#xs.option value=2}}Inactive{{/xs.option}} +{{/x-select}} +``` + +If you're using a lower version of Ember, `emberx-select` will continue +to work without block params for the forseeable future. + ### Multiselect As of version 1.1.0, `emberx-select` supports the `multiple` diff --git a/addon/components/x-option.js b/addon/components/x-option.js index 08daedf..1c0105e 100644 --- a/addon/components/x-option.js +++ b/addon/components/x-option.js @@ -56,10 +56,13 @@ export default Ember.Component.extend({ Ember.run.scheduleOnce('afterRender', this, 'registerWithXSelect'); }, + select: Ember.computed(function() { + return this.nearestOfType(XSelectComponent); + }), + registerWithXSelect() { - var select = this.nearestOfType(XSelectComponent); + const select = this.get('select'); Ember.assert("x-option component declared without enclosing x-select", !!select); - this.set('select', select); select.registerOption(this); }, diff --git a/addon/templates/current/components/x-select.hbs b/addon/templates/current/components/x-select.hbs new file mode 100644 index 0000000..83ee33b --- /dev/null +++ b/addon/templates/current/components/x-select.hbs @@ -0,0 +1,5 @@ +{{yield + (hash + option=(component "x-option" select=this) + ) +}} diff --git a/app/templates/components/x-select.hbs b/addon/templates/less-than-2.3/components/x-select.hbs similarity index 100% rename from app/templates/components/x-select.hbs rename to addon/templates/less-than-2.3/components/x-select.hbs diff --git a/app/templates/components/x-select.js b/app/templates/components/x-select.js new file mode 100644 index 0000000..97a7599 --- /dev/null +++ b/app/templates/components/x-select.js @@ -0,0 +1 @@ +export { default } from 'emberx-select/templates/components/x-select'; diff --git a/config/ember-try.js b/config/ember-try.js index 83dab0f..42c7cfe 100644 --- a/config/ember-try.js +++ b/config/ember-try.js @@ -4,6 +4,34 @@ module.exports = { name: 'default', dependencies: { } }, + { + name: 'ember-1-13', + dependencies: { + "ember": "^1.13", + }, + resolutions: { + "ember": "^1.13" + } + }, + { + // ember 2 before contextual components + name: 'ember-2-2', + dependencies: { + "ember": "~2.2.0", + }, + resolutions: { + "ember": "~2.2.0" + } + }, + { + name: 'ember-2-3', + dependencies: { + "ember": "~2.3.0", + }, + resolutions: { + "ember": "~2.3.0" + } + }, { name: 'ember-release', dependencies: { diff --git a/index.js b/index.js index e4a29b1..4c64de5 100644 --- a/index.js +++ b/index.js @@ -2,5 +2,29 @@ 'use strict'; module.exports = { - name: 'emberx-select' + name: 'emberx-select', + + hasContextualComponents: function () { + var VersionChecker = require('ember-cli-version-checker'); + + var checker = new VersionChecker(this); + var dep = checker.for('ember', 'bower'); + + var isBetaOrCanary = ['beta', 'canary'].filter(function(version) { + return dep.version.indexOf(version) >= 0; + }); + + return !!(dep.satisfies('>= 2.3.0 < 3.0.0') || isBetaOrCanary.length > 0); + }, + + treeForAddonTemplates: function treeForAddonTemplates(tree) { + var path = require('path'); + var baseTemplatesPath = path.join(this.root, 'addon/templates'); + + if (this.hasContextualComponents()) { + return this.treeGenerator(path.join(baseTemplatesPath, 'current')); + } else { + return this.treeGenerator(path.join(baseTemplatesPath, 'less-than-2.3')); + } + } }; diff --git a/package.json b/package.json index cf580a1..910e440 100644 --- a/package.json +++ b/package.json @@ -19,13 +19,12 @@ "license": "MIT", "devDependencies": { "broccoli-asset-rev": "^2.1.2", - "ember-cli": "1.13.8", + "ember-cli": "^1.13.8", "ember-cli-app-version": "0.5.0", "ember-cli-content-security-policy": "0.4.0", "ember-cli-dependency-checker": "^1.0.1", "ember-cli-github-pages": "0.0.6", - "ember-cli-htmlbars": "0.7.9", - "ember-cli-htmlbars-inline-precompile": "^0.2.0", + "ember-cli-htmlbars-inline-precompile": "^0.3.1", "ember-cli-ic-ajax": "0.2.1", "ember-cli-inject-live-reload": "^1.3.1", "ember-cli-mocha": "0.9.8", @@ -43,7 +42,9 @@ "select" ], "dependencies": { - "ember-cli-babel": "^5.1.3" + "ember-cli-babel": "^5.1.3", + "ember-cli-htmlbars": "^1.0.0", + "ember-cli-version-checker": "^1.1.6" }, "ember-addon": { "demoURL": "https://thefrontside.github.io/emberx-select", diff --git a/tests/.jshintrc b/tests/.jshintrc index d99a486..a4676ff 100644 --- a/tests/.jshintrc +++ b/tests/.jshintrc @@ -1,5 +1,6 @@ { "predef": [ + "expect", "document", "window", "location", diff --git a/tests/index.html b/tests/index.html index 8fea6fe..e0ed521 100644 --- a/tests/index.html +++ b/tests/index.html @@ -26,6 +26,7 @@ + {{content-for 'body-footer'}} {{content-for 'test-body-footer'}} diff --git a/tests/integration/with-contextual-components/contextual-component-test.js b/tests/integration/with-contextual-components/contextual-component-test.js new file mode 100644 index 0000000..ee64a92 --- /dev/null +++ b/tests/integration/with-contextual-components/contextual-component-test.js @@ -0,0 +1,26 @@ +import { it, describeComponent } from 'ember-mocha'; +import hbs from 'htmlbars-inline-precompile'; +import Ember from 'ember'; + +const integration = true; + +function hasContextualComponents() { + return !Ember.VERSION.match(/^2\.[0-2]\./) && !Ember.VERSION.match(/^1/); +} + +describeComponent('x-select', 'X-Select - Contextual Component', {integration}, function() { + + if (hasContextualComponents()) { + it('yields a hash with the option component', function() { + this.render(hbs` + {{#x-select value=status as |xs|}} + {{#xs.option value="in the office"}}In the Office{{/xs.option}} + {{#xs.option value="out of office"}}Out of Office{{/xs.option}} + {{/x-select}} + `); + + expect(this.$('option').length).to.equal(2); + }); + } +}); +