Skip to content

Commit

Permalink
feat: simplify all the things
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Removed label values and made the action simple, passing only a single boolean value.

Fixes the value change bug. More changes to come.
  • Loading branch information
Ilya Radchenko authored and knownasilya committed May 18, 2017
1 parent a7464b1 commit eee5f25
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 171 deletions.
132 changes: 20 additions & 112 deletions addon/components/x-toggle/component.js
Original file line number Diff line number Diff line change
@@ -1,77 +1,18 @@
import Ember from 'ember';
import layout from './template';

const { A, computed, typeOf } = Ember; // jshint ignore:line
const { computed } = Ember; // jshint ignore:line

const xToggle = Ember.Component.extend({
const ToggleComponent = Ember.Component.extend({
layout: layout,
classNames: ['x-toggle-component'],

name: 'default',
disabled: false,
value: 'off',
onLabel: undefined,
offLabel: undefined,
value: false,

_on: computed('onLabel', function() {
const { onLabel, defaultOnLabel } = this.getProperties('onLabel', 'defaultOnLabel');
return typeOf(onLabel) === 'undefined' ? defaultOnLabel : onLabel;
}),

_off: computed('offLabel', function() {
const { offLabel, defaultOffLabel } = this.getProperties('offLabel', 'defaultOffLabel');
return typeOf(offLabel) === 'undefined' ? defaultOffLabel : offLabel;
}),

toggled: computed('value', 'onValue', 'offValue', function() {
const { value, _onValue, _offValue } = this.getProperties('value', '_onValue', '_offValue');
const validValues = A([_onValue, _offValue]);

if (validValues.includes(value)) {
return value === _onValue;
} else {
return undefined;
}
}),

invalidState: computed('toggled', function() {
return Ember.typeOf(this.get('toggled')) === 'undefined' ? ' invalid-state' : '';
}),

_preferBoolean(value) {
if (value === 'true') {
return true;
}

if (value === 'false') {
return false;
}

return value;
},

_onValue: computed('_on', function () {
const attrs = String(this.get('_on') || '').split('::');

return this._preferBoolean(attrs.length === 1 ? attrs[0] : attrs[1]);
}),

_onLabel: computed('_on', function () {
const _on = String(this.get('_on')) || '';
return _on.split('::')[0];
}),

_offValue: computed('_off', function () {
const attrs = String(this.get('_off') || '').split('::');

return this._preferBoolean(attrs.length === 1 ? attrs[0] : attrs[1]);
}),

_offLabel: computed('_off', function () {
const _off = String(this.get('_off')) || '';

return _off.split('::')[0];
}),
// private
toggled: computed.readOnly('value'),

themeClass: computed('theme', function () {
var theme = this.get('theme') || 'default';
Expand All @@ -83,66 +24,33 @@ const xToggle = Ember.Component.extend({
return this.get('elementId') + '-x-toggle';
}),

sendToggle(value) {
let onToggle = this.get('onToggle');

if (typeof onToggle === 'function') {
onToggle(value);
}
},

actions: {
onClick(e) {
const { value, _offValue, _onValue } = this.getProperties('value', '_offValue', '_onValue');
const currentState = value === _onValue;
const oldValue = currentState ? _onValue : _offValue;
const newValue = currentState ? _offValue : _onValue;
let value = this.get('value');

e.stopPropagation();
e.preventDefault();

this.ddau('onToggle', {
code: 'toggled',
oldValue: oldValue,
newValue: newValue,
context: this
}, newValue);
this.sendToggle(!value);
},

setToValue(state, e) {
const { toggled, _offValue, _onValue } = this.getProperties('toggled', '_offValue', '_onValue');

setToValue(value, e) {
e.stopPropagation();
e.preventDefault();

if (toggled !== state) {
this.ddau('onToggle', {
code: 'set',
oldValue: state ? _offValue : _onValue,
newValue: !state ? _offValue : _onValue,
context: this
}, !state ? _offValue : _onValue);
}
}
},

/**
* Provide a DDAU "action" or "mut" response
* @param {string } action The name of the exposed action property
* @param {hash} hash A hash of attributes that are passed back to a "action"
* @param {mixed} value A value that is passed to the "update" function (aka, mut helper) if available
* @return {boolean} Pass back true if `mut` not used; if used then proxies mut's response back
*/
ddau(action, hash, value) {
if (this.get('disabled')) {
return;
}

if (this.attrs[action] && this.attrs[action].update) {
this.attrs[action].update(value);
return true;
} else if (this.attrs[action]) {
return this.attrs[action](hash);
} else {
// assume that container is using old-style actions
this.sendAction(action, hash);
return undefined;

this.sendToggle(value);
}
}
});

xToggle[Ember.NAME_KEY] = 'x-toggle';
ToggleComponent[Ember.NAME_KEY] = 'x-toggle';

export default xToggle;
export default ToggleComponent;
31 changes: 13 additions & 18 deletions addon/components/x-toggle/template.hbs
Original file line number Diff line number Diff line change
@@ -1,36 +1,31 @@
{{#if showLabels}}
<label id='off-label-{{elementId}}' for={{forId}} class="toggle-text toggle-prefix off-label" onclick={{action 'setToValue' false}}>
{{_offLabel}}
<label id='off-label-{{elementId}}' for={{forId}} class="toggle-text toggle-prefix off-label"
onclick={{action 'setToValue' false}}>
{{offLabel}}
</label>
{{/if}}

<span
class="x-toggle-container {{size}}{{if toggled ' x-toggle-container-checked'}}{{if disabled ' x-toggle-container-disabled'}}"
>
<input
id="input-{{elementId}}"
<span class="x-toggle-container {{size}}{{if value ' x-toggle-container-checked'}}{{if disabled ' x-toggle-container-disabled'}}">
<input id="input-{{elementId}}"
name={{name}}
type='checkbox'
checked={{toggled}}
changed={{action 'setToValue' value='target.checked'}}
class='x-toggle'
disabled={{disabled}}
>
disabled={{disabled}}>

<div
id="visual-{{elementId}}"
class="x-toggle-btn {{themeClass}} {{size}}{{if disabled ' x-toggle-disabled'}}{{invalidState}}"
data-tg-off={{_offLabel}}
data-tg-on={{_onLabel}}
<div id="visual-{{elementId}}"
class="x-toggle-btn {{themeClass}} {{size}}{{if disabled ' x-toggle-disabled'}}"
onclick={{action 'onClick'}}
for="input-{{elementId}}"
aria-role="checkbox"
aria-owns="input-{{elementId}}"
>
aria-owns="input-{{elementId}}">
</div>
</span>

{{#if showLabels}}
<label id='on-label-{{elementId}}' for="input-{{elementId}}" class="toggle-text toggle-postfix {{size}} on-label" onclick={{action 'setToValue' true}}>
{{_onLabel}}
<label id='on-label-{{elementId}}' for="input-{{elementId}}" class="toggle-text toggle-postfix {{size}} on-label"
onclick={{action 'setToValue' true}}>
{{onLabel}}
</label>
{{/if}}
78 changes: 37 additions & 41 deletions tests/integration/components/x-toggle/component-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,23 @@ test('it renders', function(assert) {

test('changing disabled property disables component', function(assert) {
this.set('disabled', true);
this.set('value', false);
this.render(hbs`{{x-toggle
value='off'
value=value
disabled=disabled
}}`);
assert.equal($('input.x-toggle').prop('disabled'), true);
assert.equal(this.get('value'), false);
this.$('div.x-toggle-btn').click();
assert.equal($('input.x-toggle').prop('disabled'), true);
assert.equal(this.get('value'), false);
});

test('clicking component triggers onToggle action', function(assert) {
const done = assert.async();
this.set('myValue', 'off');
this.on('onToggle', hash => {
assert.equal(hash.code, 'toggled', 'onToggle code is toggle');
assert.equal(hash.oldValue, 'off', 'proper old value');
assert.equal(hash.newValue, 'on', 'proper new value');
this.set('myValue', false);
this.on('onToggle', val => {
assert.equal(val, true, 'new value set');

this.set('completed', true);
done();
Expand All @@ -44,52 +47,30 @@ test('clicking component triggers onToggle action', function(assert) {
done();
}
}, 1000);

});

if (emberVersionGTE(2,0)) {
test('clicking component works with default on/off and mut helper', function(assert) {
this.set('value', 'off');
this.render(hbs`{{x-toggle
value=value
onToggle=(mut value)
}}`);
assert.equal(this.get('value'), 'off');
this.$('div.x-toggle-btn').click();
assert.equal(this.get('value'), 'on');
});

test('component toggles when container changes value', function(assert) {
this.set('value', 'off');
this.render(hbs`{{x-toggle
value=value
onToggle=(mut value)
}}`);
assert.equal(this.get('value'), 'off');
this.set('value', 'on');
assert.equal(this.get('value'), 'on');
});

test('clicking component works with bespoke values and mut helper', function(assert) {
this.set('value', 'foo');
this.set('value', false);
this.render(hbs`{{x-toggle
value=value
offLabel='Foo::foo'
onLabel='Bar::bar'
onToggle=(mut value)
offLabel='Foo'
onLabel='Bar'
showLabels=true
onToggle=(action (mut value))
}}`);
assert.equal(this.get('value'), 'foo');
assert.equal(this.$('div.x-toggle-btn').data('tg-off'), 'Foo', '"off" property set on toggle');
assert.equal(this.$('div.x-toggle-btn').data('tg-on'), 'Bar', '"on" property set on toggle');
assert.equal(this.get('value'), false);
assert.equal(this.$('.off-label').text().trim(), 'Foo', '"off" property set on toggle');
assert.equal(this.$('.on-label').text().trim(), 'Bar', '"on" property set on toggle');
this.$('div.x-toggle-btn').click();
assert.equal(this.get('value'), 'bar', 'click toggles value');
assert.equal(this.get('value'), true, 'click toggles value');
});

test('clicking component works with boolean true/false', function(assert) {
this.set('value', false);
this.render(hbs`{{x-toggle
value=value
onToggle=(mut value)
onToggle=(action (mut value))
onLabel=true
offLabel=false
}}`);
Expand All @@ -102,12 +83,27 @@ if (emberVersionGTE(2,0)) {
this.set('value', false);
this.render(hbs`{{x-toggle
value=value
onToggle=(mut value)
onLabel='Yes::true'
offLabel='No::false'
onToggle=(action (mut value))
onLabel='Yes'
offLabel='No'
}}`);
assert.equal(this.get('value'), false);
this.$('div.x-toggle-btn').click();
assert.equal(this.get('value'), true);
});

test('value can be set by changing the value property', function(assert) {
this.set('value', false);
this.set('show', false);

this.render(hbs`
{{x-toggle value=value onToggle=(action (mut value))}}
`);

this.set('value', true);
assert.equal(this.$('.x-toggle-container').hasClass('x-toggle-container-checked'), true);

this.set('value', false);
assert.equal(this.$('.x-toggle-container').hasClass('x-toggle-container-checked'), false);
});
}

0 comments on commit eee5f25

Please sign in to comment.