Skip to content

Commit

Permalink
[feature] add contextual components support
Browse files Browse the repository at this point in the history
Contextual components provide a nice syntax for encapsulating components
that would not be much use elsewhere. `x-option` is such a component; it
is not so useful without `x-select`. Indeed, we even assert if there is
no parent `x-select` component.

Because we want to provide a great experience for our users who don't
yet have the time to upgrade Ember, I implemented this using ember-cli's
`treeForAddonTemplates` hook. If the consuming app has a version of
ember that is less than `2.3.0`, the original template is provided to
the app tree. Otherwise, we can take advantage of contextual components
and provide a template that yields a hash with `x-option` for users
to use. I stole this from Ember CLI Version Checker's ["Real World Example"](https://github.com/rwjblue/ember-cli-version-checker/blob/0040430d799c783731301773aeeb32b76f936585/README.md#real-world-example)
in their README.
  • Loading branch information
Stanley Stuart committed Mar 10, 2016
1 parent c923be9 commit c6f02d1
Show file tree
Hide file tree
Showing 11 changed files with 106 additions and 6 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
6 changes: 5 additions & 1 deletion addon/components/x-option.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,12 @@ export default Ember.Component.extend({
Ember.run.scheduleOnce('afterRender', this, 'registerWithXSelect');
},

parentXSelect: Ember.computed(function() {
return this.nearestOfType(XSelectComponent);
}),

registerWithXSelect() {
var select = this.nearestOfType(XSelectComponent);
const select = this.get('parentXSelect');
Ember.assert("x-option component declared without enclosing x-select", !!select);
this.set('select', select);
select.registerOption(this);
Expand Down
5 changes: 5 additions & 0 deletions addon/templates/current/components/x-select.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{{yield
(hash
option=(component "x-option" parentXSelect=this)
)
}}
File renamed without changes.
1 change: 1 addition & 0 deletions app/templates/components/x-select.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'emberx-select/templates/components/x-select';
18 changes: 18 additions & 0 deletions config/ember-try.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@ module.exports = {
name: 'default',
dependencies: { }
},
{
name: 'ember-1-13',
dependencies: {
"ember": "^1.13",
},
resolutions: {
"ember": "^1.13"
}
},
{
name: 'ember-2-before-contextual-components',
dependencies: {
"ember": "~2.2.0",
},
resolutions: {
"ember": "~2.2.0"
}
},
{
name: 'ember-release',
dependencies: {
Expand Down
1 change: 1 addition & 0 deletions ember-cli-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
var EmberApp = require('ember-cli/lib/broccoli/ember-addon');

module.exports = function(defaults) {
var semver = require('semver');
var app = new EmberApp(defaults, {
// Add options here
});
Expand Down
26 changes: 25 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'));
}
}
};
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
1 change: 1 addition & 0 deletions tests/.jshintrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"predef": [
"expect",
"document",
"window",
"location",
Expand Down
Original file line number Diff line number Diff line change
@@ -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-3]/) && !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);
});
}
});

0 comments on commit c6f02d1

Please sign in to comment.