Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update actions to kebob case & arguments. #159

Merged
merged 4 commits into from
Oct 12, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ before_install:
- npm install -g bower
- npm install -g yarn
- bower --version
- yarn add phantomjs-prebuilt
- npm i phantomjs-prebuilt
- phantomjs --version
install:
- yarn
Expand Down
56 changes: 26 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,6 @@ through data rather than through the DOM.
{{/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 All @@ -80,28 +77,29 @@ its selections directly on that array.

The selections array will be initialized to an empty array if not present.

## Action and Action Arguments
## Actions and Action Arguments

The action that is dispatched by x-select whenever the selected value or values
change (change event) has a function signature of:
All of x-selects actions are closure actions. This means you must use
the `action` helper (i.e. `on-click=(action "onClick")`). The function
that is dispatched by x-select whenever the event fires has a function
signature of:

```js
/**
* @param value {Object} the value selected by the user.
* @param component {Ember.Component} the x-select component itself
* @param {Object} value - the value selected by the user.
* @param {Object} event - the jQuery event of the action
*/
function (value, component) {
function (value, event) {
// action body...
}
```

Most of the time all you need is the value that has been selected, but
sometimes your action requires more context than just that. In those
cases, you can associate arbitrary attributes with the component
itself and use them later inside your action handler. For example:
cases, you can pass any arguments you need from the template. For example:

```handlebars
{{#x-select action="didMakeSelection" default=anything as |xs|}}
{{#x-select on-click=(action "didMakeSelection" isXSelectRequired) required=isXSelectRequired as |xs|}}
<option>Nothing</option>
{{#xs.option value=something}}Something{{/xs.option}}
{{/x-select}}
Expand All @@ -111,38 +109,36 @@ then, inside your action handler:
```js
export default Ember.Route.extend({
actions: {
didMakeSelection: function(selection, component) {
if (selection) {
this.set('selection', selection)
didMakeSelection(value, event, isXSelectRequired) {
if (!value & isXSelectRequired) {
this.set('error', 'You must fill out this field');
} else {
this.set('selection', component.get('default'))
this.set('selection', value);
}
}
}
});
```

#### Other Actions

x-select also provides other actions that fire on different event
x-select provides other actions that fire on different event
types. These actions follow the HTML input event naming convention.

**onblur**
**on-blur**

`onblur` fires anytime the `blur` event is triggered on the x-select
component. When the action fires it sends three arguments: the
component, the value, and the jQuery event.
`on-blur` fires anytime the `blur` event is triggered on the x-select
component. When the action fires it sends two arguments: the value,
the jQuery event.

**onfocusout**
**on-focus-out**

`onfocusout` fires anytime the `focusOut` event is triggered on the x-select
component. When the action fires it sends three arguments: the
component, the value, and the jQuery event.
`on-focus-out` fires anytime the `focusOut` event is triggered on the x-select
component. When the action fires it sends two arguments: the value,
the jQuery event.

**onclick**
**on-click**

`onclick` fires when x-select is clicked. When the action fires it
sends three arguments: the component, the value, and the jQuery event.
`on-click` fires when x-select is clicked. When the action fires it
sends two arguments: the value, the jQuery event.

**on-disable** (x-option)

Expand Down
74 changes: 63 additions & 11 deletions addon/components/x-select.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,18 @@ export default Ember.Component.extend({
*
* @property disabled
* @type Boolean
* @default null
* @default false
*/
disabled: null,
disabled: false,

/**
* Bound to the `multiple` attribute on the native <select> tag.
*
* @property multiple
* @type Boolean
* @default null
* @default false
*/
multiple: null,
multiple: false,

/**
* The collection of options for this select box. When options are
Expand All @@ -71,39 +71,91 @@ export default Ember.Component.extend({
*/
tabindex: 0,

/**
* Function for the `on-blur` action
*
* @property on-blur
* @type Function
*/
"on-blur": Ember.K,

/**
* Function for the `on-click` action
*
* @property on-click
* @type Function
*/
"on-click": Ember.K,

/**
* Function for the `on-change` action
*
* @property on-change
* @type Function
*/
"on-change": Ember.K,

/**
* Function for the `on-focus-out` action
*
* @property on-focus-out
* @type Function
*/
"on-focus-out": Ember.K,

/**
* Function that calls an action and sends the proper arguments.
*
* @method _handleAction
* @type Function
* @param {String} action - string name of the action to invoke
* @param {String|Object} value - current value of the component
* @param {Object} event - jQuery event from the current action
*/
_handleAction(action, value, event) {
let actionValue = this.get(action);

if(typeof actionValue === 'string') {
Ember.warn(`x-select: You must use the action helper for all actions. The try: ${action}=(action "${actionValue}") in your template`, false, {id: 'x-select-string-action'});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"The try: "

Seems like a typo?

return;
}

this.get(action)(value, event);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the right track, but I think we can make it a bit clearer and improve the error handling even more.

  1. _handleAction isn't really what it's doing. It seems like it's dispatching the action so that something else can handle it. callAction, invokeAction, doAction would be better candidates.
  2. We issue a warning if actionValue is a string, but it if we're going to go to the effort of performing an explicit check on the type of the action value, then we should issue a warning if it's anything except a function. Because anything except a function will fail.
  3. If we've already got the action in the form of actionValue, then let's just call it:
actionValue(value, event);

},

/**
* When the select DOM event fires on the element, trigger the
* component's action with the current value.
*/
change(event) {
let nextValue = this._getValue();

this.sendAction('action', nextValue, this);
this.sendAction('onchange', this, nextValue, event);
this.sendAction('action', nextValue, event, this);
this._handleAction('on-change', nextValue, event);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this dispatch correctly? I'm concerned that bubbling an action with sendAction AND calling the closure action could just dispatch the action twice? Is sendAction a no-op if a closure action is passed in or something?

},

/**
* When the click DOM event fires on the element, trigger the
* component's action with the component, x-select value, and the jQuery event.
*/
click(event) {
this.sendAction('onclick', this, this._getValue(), event);
this._handleAction('on-click', this._getValue(), event);
},

/**
* When the blur DOM event fires on the element, trigger the
* component's action with the component, x-select value, and the jQuery event.
*/
blur(event) {
this.sendAction('onblur', this, this._getValue(), event);
this._handleAction('on-blur', this._getValue(), event);
},

/**
* When the focusOut DOM event fires on the element, trigger the
* component's action with the component, x-select value, and the jQuery event.
*/
focusOut(event) {
this.sendAction('onfocusout', this, this._getValue(), event);
this._handleAction('on-focus-out', this._getValue(), event);
},

/**
Expand All @@ -125,7 +177,7 @@ export default Ember.Component.extend({
*
* @private
* @return {Array} all the values from selected x-options
*/
*/
_findMultipleValues() {
return this.get('options').filter(isSelectedOption).map(option => option.get('value'));
},
Expand All @@ -136,7 +188,7 @@ export default Ember.Component.extend({
*
* @private
* @return {Object} the value of the first select `x-option`, or null
*/
*/
_findSingleValue() {
let selectedValue = this.get('options').find(isSelectedOption);
return selectedValue ? selectedValue.get('value') : null;
Expand Down
2 changes: 1 addition & 1 deletion tests/dummy/app/controllers/events/blur.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Ember from 'ember';

export default Ember.Controller.extend({
actions: {
onBlur(component, value, event) {
onBlur(value, event) {
this.set('eventType', event.type);
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/dummy/app/controllers/events/click.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Ember from 'ember';

export default Ember.Controller.extend({
actions: {
onClick(component, value, event) {
onClick(value, event) {
this.set('eventType', event.type);
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/dummy/app/controllers/events/focus-out.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Ember from 'ember';

export default Ember.Controller.extend({
actions: {
focusOut(component, value, event) {
focusOut(value, event) {
this.set('eventType', event.type);
}
}
Expand Down
4 changes: 2 additions & 2 deletions tests/dummy/app/controllers/single.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export default Ember.Controller.extend(Folks, {
attrSize: null,

actions: {
tagYouAreIt: function(object) {
this.set('it', object);
tagYouAreIt: function(value) {
this.set('it', value);
}
}
});
2 changes: 1 addition & 1 deletion tests/dummy/app/templates/events/blur.hbs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<h2>Blur</h2>

{{#x-select value=it onblur="onBlur" as |xs|}}
{{#x-select value=it on-blur=(action "onBlur") as |xs|}}
{{#xs.option value=charles}}Charles{{/xs.option}}
{{#xs.option value=bastion}}Bastion{{/xs.option}}
{{#xs.option value=stanley}}Stanley{{/xs.option}}
Expand Down
2 changes: 1 addition & 1 deletion tests/dummy/app/templates/events/click.hbs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<h2>Click</h2>

{{#x-select value=it onclick="onClick" as |xs|}}
{{#x-select value=it on-click=(action "onClick") as |xs|}}
{{#xs.option value=charles}}Charles{{/xs.option}}
{{#xs.option value=bastion}}Bastion{{/xs.option}}
{{#xs.option value=stanley}}Stanley{{/xs.option}}
Expand Down
2 changes: 1 addition & 1 deletion tests/dummy/app/templates/events/focus-out.hbs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<h2>Focus Out</h2>

{{#x-select value=it onfocusout="focusOut" as |xs|}}
{{#x-select value=it on-focus-out=(action "focusOut") as |xs|}}
{{#xs.option value=charles}}Charles{{/xs.option}}
{{#xs.option value=bastion}}Bastion{{/xs.option}}
{{#xs.option value=stanley}}Stanley{{/xs.option}}
Expand Down
45 changes: 45 additions & 0 deletions tests/integration/components/default-value-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* jshint expr:true */
import { expect } from 'chai';
import { describeComponent, it } from 'ember-mocha';
import { describe, beforeEach } from 'mocha';
import hbs from 'htmlbars-inline-precompile';

describeComponent(
'default-value',
'Integration: DefaultValue',
{
integration: true
},
function() {
describe("default value of null", function() {
beforeEach(function() {
this.set('make', null);
this.set('selectAction', (value) => {
this.set("make", value);
this.set("wasCalled", true);
});

this.render(hbs`
{{#x-select value=make action=selectAction as |xs|}}
{{#xs.option value="fordValue" class="spec-ford-option"}}Ford{{/xs.option}}
{{#xs.option value="chevyValue"}}Chevy{{/xs.option}}
{{#xs.option value="dodgeValue" class="spec-dodge-option"}}Dodge{{/xs.option}}
{{/x-select}}
`);
});

it("displays the first item in the list", function() {
expect(this.$('select option:selected').text()).to.equal("Ford");
});

it("sets the default value to the first element", function() {
expect(this.get('make')).to.equal("fordValue");
});

it("invokes the select action on init", function() {
expect(this.get("wasCalled")).to.equal(true);
});

});
}
);
Loading