Skip to content
Closed
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
72 changes: 72 additions & 0 deletions app/components/github-issue-select.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import Ember from 'ember';

const {
Component,
computed,
get
} = Ember;

/**
* A component holding a power-select dropdown, used to select a github
* issue for a task.
*
* Expects a `task` and an `issues` attribute to be provided.
* `issues` can be a promise.
*
* @class GithubIssueSelectComponent
* @module code-corps-ember/components/github-issue-select
* @extends Ember.Component
* @public
*/
export default Component.extend({
classNames: ['github-issue-select'],

/**
* Assignable collection to be rendered as options in a dropdown. Can be
* a promise. The power-select component will render a loading state for
* that promise.
*
* @property issues
* @public
*/
issues: [],

/**
* Assignable property. A {DS.Model} should be assigned to it.
* Used to determine if the component should be enabled/disabled and to
* preselect an option in the dropdown.
*
* @property task
* @public
*/
task: null,

/**
* A computed property, returns an object from `issues` which matches
* `task.githubId`
*
* @property selectedIssue
* @private
*/
selectedIssue: computed('issues', 'task.githubId', {
get() {
let githubId = get(this, 'task.githubId');
let issues = get(this, 'issues');

return issues.find((issue) => {
return get(issue, 'githubId') == githubId;
});
}
}),

/**
* Default handler for the power-select change action.
*
* power-select requires some sort of function, so the usual `null` default
* does not work.
*
* @method onIssueSelected
* @public
*/
onIssueSelected() {}
});
72 changes: 72 additions & 0 deletions app/components/github-repository-select.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import Ember from 'ember';

const {
Component,
computed,
get
} = Ember;

/**
* A component holding a power-select dropdown, used to select a github
* repository for a project.
*
* Expects a `project` and a `repositories` attribute to be provided.
* `repositories` can be a promise.
*
* @class GithubRepositorySelectComponent
* @module code-corps-ember/components/github-repository-select
* @extends Ember.Component
* @public
*/
export default Component.extend({
classNames: ['github-repository-select'],

/**
* Assignable collection to be rendered as options in a dropdown. Can be
* a promise. The power-select component will render a loading state for
* that promise.
*
* @property repositories
* @public
*/
repositories: [],

/**
* Assignable property. A {DS.Model} should be assigned to it.
* Used to determine if the component should be enabled/disabled and to
* preselect an option in the dropdown.
*
* @property project
* @public
*/
project: null,

/**
* A computed property, returns an object from `repositories` which matches
* `project.githubId`
*
* @property selectedRepository
* @private
*/
selectedRepository: computed('repositories', 'project.githubId', {
get() {
let githubId = get(this, 'project.githubId');
let repositories = get(this, 'repositories');

return repositories.find((repo) => {
return get(repo, 'githubId') == githubId;
});
}
}),

/**
* Default handler for the power-select change action.
*
* power-select requires some sort of function, so the usual `null` default
* does not work.
*
* @method onRepoSelected
* @public
*/
onRepoSelected() {}
});
11 changes: 11 additions & 0 deletions app/templates/components/github-issue-select.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{{#power-select
disabled=task.githubId
loadingMessage='Retrieving issues...'
options=issues
onchange=onIssueSelected
placeholder='Connect your task with a GitHub issue'
renderInPlace=true
Copy link
Contributor

Choose a reason for hiding this comment

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

So we have a power-select/component.js where we define our common defaults....loadingMessage, searchMessage, etc. If all power selects are renderInPlace=true, then this would be a great place to do it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not all of them are. The one for task assignment is causing layout problems if we render in place, due to parent scroll bars.

There is, however, a fix listed in the power-select changelog that may have resolved it. We could look into it, but probably not as part of this issue.

selected=selectedIssue
as |issue|}}
{{issue.issueName}}
{{/power-select}}
11 changes: 11 additions & 0 deletions app/templates/components/github-repository-select.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{{#power-select
disabled=project.githubId
loadingMessage='Retrieving repositories...'
options=repositories
onchange=onRepoSelected
placeholder='Connect your project with a GitHub repository'
renderInPlace=true
selected=selectedRepository
as |repository|}}
{{repository.repositoryName}}
{{/power-select}}
2 changes: 1 addition & 1 deletion app/templates/components/task-card.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
</p>
{{#if canAssign}}
{{#power-select
beforeOptionsComponent=(component "power-select/before-options" selectRemoteController=selectRemoteController)
beforeOptionsComponent=(component "power-select/before-task-options" selectRemoteController=selectRemoteController)
buildSelection=(action "buildSelection")
class="select-inline"
disabled=userSelectDisabled
Expand Down
100 changes: 100 additions & 0 deletions tests/integration/components/github-issue-select-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import PageObject from 'ember-cli-page-object';
import pageObject from '../../pages/components/github-issue-select';
import Ember from 'ember';

const { set, run } = Ember;

const page = PageObject.create(pageObject);

moduleForComponent('github-issue-select', 'Integration | Component | github issue select', {
integration: true,
beforeEach() {
page.setContext(this);
// defaults
set(this, 'issues', []);
set(this, 'onIssueSelected', () => {});
renderPage();
},
afterEach() {
page.removeContext();
}
});

function renderPage() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it make sense to just inline page.render in the beforeEach?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It actually would. I started the implementation thinking I'd be calling renderPage() for each test individually.

page.render(hbs`
{{github-issue-select onIssueSelected=onIssueSelected task=task issues=issues}}
`);
}

test('it renders options', function(assert) {
assert.expect(2);

let issues = [
{ issueName: 'Issue 1', githubId: 1 },
{ issueName: 'Issue 2', githubId: 2 }
];

run(() => set(this, 'issues', issues));

page.openDropdown();

assert.equal(page.issues(0).text, 'Issue 1');
assert.equal(page.issues(1).text, 'Issue 2');
});

test('it triggers action on selection', function(assert) {
assert.expect(2);

let issues = [
{ issueName: 'Issue 1', githubId: 1 },
{ issueName: 'Issue 2', githubId: 2 }
];

run(() => set(this, 'issues', issues));

let [issue1, issue2] = issues;

let assertIssue1 = (issue) => assert.deepEqual(issue, issue1, 'First issue was sent as part of action.');
run(() => set(this, 'onIssueSelected', assertIssue1));
page.openDropdown().issues(0).select();

let assertIssue2 = (issue) => assert.deepEqual(issue, issue2, 'Second issue was sent as part of action.');
run(() => set(this, 'onIssueSelected', assertIssue2));
page.openDropdown().issues(1).select();
});

test('it renders as disabled if task is connected', function(assert) {
assert.expect(2);

let issues = [
{ issueName: 'Issue 1', githubId: 1 },
{ issueName: 'Issue 2', githubId: 2 }
];

run(() => set(this, 'issues', issues));

run(() => set(this, 'task', { githubId: 1 }));
assert.ok(page.disabled, 'Selection is disabled.');

run(() => set(this, 'task', { githubId: null }));
assert.notOk(page.disabled, 'Selection is not disabled.');
});

test('it renders proper selection status if task is connected', function(assert) {
assert.expect(2);

let issues = [
{ issueName: 'Issue 1', githubId: 1 },
{ issueName: 'Issue 2', githubId: 2 }
];

run(() => set(this, 'issues', issues));

run(() => set(this, 'task', { githubId: 1 }));
assert.equal(page.selectedIssue.text, 'Issue 1', 'Issue name is rendered');

run(() => set(this, 'task', { githubId: null }));
assert.equal(page.selectedIssue.text, 'Connect your task with a GitHub issue', 'Placeholder is rendered');
});
103 changes: 103 additions & 0 deletions tests/integration/components/github-repository-select-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import PageObject from 'ember-cli-page-object';
import pageObject from '../../pages/components/github-repository-select';
import Ember from 'ember';

const { set, run } = Ember;

const page = PageObject.create(pageObject);

moduleForComponent('github-repository-select', 'Integration | Component | github repository select', {
integration: true,
beforeEach() {
page.setContext(this);
// defaults
set(this, 'repositories', []);
set(this, 'onRepoSelected', () => {});
renderPage();
},
afterEach() {
page.removeContext();
}
});

function renderPage() {
page.render(hbs`
{{github-repository-select
onRepoSelected=onRepoSelected
project=project
repositories=repositories}}
`);
}

test('it renders options', function(assert) {
assert.expect(2);

let repositories = [
{ repositoryName: 'Repo 1', githubId: 1 },
{ repositoryName: 'Repo 2', githubId: 2 }
];

run(() => set(this, 'repositories', repositories));

page.openDropdown();

assert.equal(page.repositories(0).text, 'Repo 1');
assert.equal(page.repositories(1).text, 'Repo 2');
});

test('it triggers action on selection', function(assert) {
assert.expect(2);

let repositories = [
{ repositoryName: 'Repo 1', githubId: 1 },
{ repositoryName: 'Repo 2', githubId: 2 }
];

run(() => set(this, 'repositories', repositories));

let [repo1, repo2] = repositories;

let assertRepo1 = (repo) => assert.deepEqual(repo, repo1, 'First repo was sent as part of action.');
run(() => set(this, 'onRepoSelected', assertRepo1));
page.openDropdown().repositories(0).select();

let assertRepo2 = (repo) => assert.deepEqual(repo, repo2, 'Second repo was sent as part of action.');
run(() => set(this, 'onRepoSelected', assertRepo2));
page.openDropdown().repositories(1).select();
});

test('it renders as disabled if project is connected', function(assert) {
assert.expect(2);

let repositories = [
{ repositoryName: 'Repo 1', githubId: 1 },
{ repositoryName: 'Repo 2', githubId: 2 }
];

run(() => set(this, 'repositories', repositories));

run(() => set(this, 'project', { githubId: 1 }));
assert.ok(page.disabled, 'Selection is disabled.');

run(() => set(this, 'project', { githubId: null }));
assert.notOk(page.disabled, 'Selection is not disabled.');
});

test('it renders proper selection status if project is connected', function(assert) {
assert.expect(2);

let repositories = [
{ repositoryName: 'Repo 1', githubId: 1 },
{ repositoryName: 'Repo 2', githubId: 2 }
];

run(() => set(this, 'repositories', repositories));

run(() => set(this, 'project', { githubId: 1 }));
assert.equal(page.selectedRepository.text, 'Repo 1', 'Repo name is rendered');

run(() => set(this, 'project', { githubId: null }));
assert.equal(page.selectedRepository.text, 'Connect your project with a GitHub repository', 'Placeholder is rendered');
});
Loading