Skip to content
This repository has been archived by the owner on Nov 28, 2022. It is now read-only.

Commit

Permalink
de-couple gh-task-button from gh-spin-button
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinansfield committed Jan 20, 2017
1 parent 1dc0fd8 commit 0aa8b81
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 17 deletions.
42 changes: 28 additions & 14 deletions app/components/gh-task-button.js
@@ -1,9 +1,8 @@
import Component from 'ember-component';
import observer from 'ember-metal/observer';
import {reads} from 'ember-computed';
import {invokeAction} from 'ember-invoke-action';

import SpinButton from './gh-spin-button';
import layout from 'ghost-admin/templates/components/gh-spin-button';

/**
* Task Button works exactly like Spin button, but with one major difference:
*
Expand All @@ -15,30 +14,45 @@ import layout from 'ghost-admin/templates/components/gh-spin-button';
* component, all running promises will automatically be cancelled when this
* component is removed from the DOM
*/
export default SpinButton.extend({
layout, // This is used to we don't have to re-implement the template

classNameBindings: ['showSpinner:appear-disabled'],
export default Component.extend({
tagName: 'button',
classNameBindings: ['isRunning:appear-disabled'],
attributeBindings: ['disabled', 'type', 'tabindex'],

task: null,

submitting: reads('task.last.isRunning'),
disabled: false,

isRunning: reads('task.last.isRunning'),

click() {
// do nothing if disabled externally
if (this.get('disabled')) {
return false;
}

let task = this.get('task');
let taskName = this.get('task.name');
let lastTaskName = this.get('task.last.task.name');

// task-buttons are never truly disabled so that clicks when a taskGroup
// is running don't get dropped however that means we need to check here
// so we don't spam actions through multiple clicks
if (this.get('showSpinner') && taskName === lastTaskName) {
// task-buttons are never disabled whilst running so that clicks when a
// taskGroup is running don't get dropped BUT that means we need to check
// here to avoid spamming actions from multiple clicks
if (this.get('isRunning') && taskName === lastTaskName) {
return;
}

invokeAction(this, 'action');

return task.perform();
}
},

setSize: observer('isRunning', function () {
if (this.get('isRunning')) {
this.$().width(this.$().width());
this.$().height(this.$().height());
} else {
this.$().width('');
this.$().height('');
}
})
});
9 changes: 9 additions & 0 deletions app/templates/components/gh-task-button.hbs
@@ -0,0 +1,9 @@
{{#if isRunning}}
<span class="spinner"></span>
{{else}}
{{#if buttonText}}
{{buttonText}}
{{else}}
{{{yield}}}
{{/if}}
{{/if}}
106 changes: 103 additions & 3 deletions tests/integration/components/gh-task-button-test.js
Expand Up @@ -3,6 +3,9 @@ import {expect} from 'chai';
import {describe, it} from 'mocha';
import {setupComponentTest} from 'ember-mocha';
import hbs from 'htmlbars-inline-precompile';
import {task, timeout} from 'ember-concurrency';
import run from 'ember-runloop';
import wait from 'ember-test-helpers/wait';

describe('Integration: Component: gh-task-button', function() {
setupComponentTest('gh-task-button', {
Expand All @@ -11,9 +14,106 @@ describe('Integration: Component: gh-task-button', function() {

it('renders', function () {
this.render(hbs`{{#gh-task-button}}Test{{/gh-task-button}}`);
expect(this.$()).to.have.length(1);
expect(this.$().text().trim()).to.equal('Test');
expect(this.$('button')).to.exist;
expect(this.$('button')).to.contain('Test');
expect(this.$('button')).to.have.prop('disabled', false);

this.render(hbs`{{#gh-task-button class="testing"}}Test{{/gh-task-button}}`);
expect(this.$('button')).to.have.class('testing');

this.render(hbs`{{#gh-task-button disabled=true}}Test{{/gh-task-button}}`);
expect(this.$('button')).to.have.prop('disabled', true);

this.render(hbs`{{#gh-task-button type="submit"}}Test{{/gh-task-button}}`);
expect(this.$('button')).to.have.attr('type', 'submit');

this.render(hbs`{{#gh-task-button tabindex="-1"}}Test{{/gh-task-button}}`);
expect(this.$('button')).to.have.attr('tabindex', '-1');
});

it('shows spinner whilst running', function (done) {
this.set('myTask', task(function* () {
yield timeout(50);
}));

this.render(hbs`{{#gh-task-button task=myTask}}Test{{/gh-task-button}}`);

this.get('myTask').perform();

run.later(this, function () {
expect(this.$('button')).to.have.descendants('span.spinner');
}, 20);

wait().then(done);
});

it('appears disabled whilst running', function (done) {
this.set('myTask', task(function* () {
yield timeout(50);
}));

this.render(hbs`{{#gh-task-button task=myTask}}Test{{/gh-task-button}}`);
expect(this.$('button'), 'initial class').to.not.have.class('appear-disabled');

this.get('myTask').perform();

run.later(this, function () {
expect(this.$('button'), 'running class').to.have.class('appear-disabled');
}, 20);

run.later(this, function () {
expect(this.$('button'), 'ended class').to.not.have.class('appear-disabled');
}, 70);

wait().then(done);
});

it('performs task on click', function (done) {
let taskCount = 0;

this.set('myTask', task(function* () {
yield timeout(50);
taskCount = taskCount + 1;
}));

this.render(hbs`{{#gh-task-button task=myTask}}Test{{/gh-task-button}}`);
this.$('button').click();

wait().then(() => {
expect(taskCount, 'taskCount').to.equal(1);
done();
});
});

// TODO: figure out how to test concurrency behavior
it('keeps button size when showing spinner', function (done) {
this.set('myTask', task(function* () {
yield timeout(50);
}));

this.render(hbs`{{#gh-task-button task=myTask}}Test{{/gh-task-button}}`);
let width = this.$('button').width();
let height = this.$('button').height();
expect(this.$('button')).to.not.have.attr('style');

this.get('myTask').perform();

run.later(this, function () {
// we can't test exact width/height because Chrome/Firefox use different rounding methods
// expect(this.$('button')).to.have.attr('style', `width: ${width}px; height: ${height}px;`);

let [widthInt] = width.toString().split('.');
let [heightInt] = height.toString().split('.');

expect(this.$('button').attr('style')).to.have.string(`width: ${widthInt}`);
expect(this.$('button').attr('style')).to.have.string(`height: ${heightInt}`);
}, 20);

run.later(this, function () {
// chai-jquery test doesn't work because Firefox outputs blank string
// expect(this.$('button')).to.not.have.attr('style');
expect(this.$('button').attr('style')).to.be.blank;
}, 70);

wait().then(done);
});
});

0 comments on commit 0aa8b81

Please sign in to comment.