-
Notifications
You must be signed in to change notification settings - Fork 78
Added github-issue-select component #1296
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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() {} | ||
| }); |
| 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() {} | ||
| }); |
| 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 | ||
| selected=selectedIssue | ||
| as |issue|}} | ||
| {{issue.issueName}} | ||
| {{/power-select}} | ||
| 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}} |
| 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() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make sense to just inline
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It actually would. I started the implementation thinking I'd be calling |
||
| 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'); | ||
| }); | ||
| 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'); | ||
| }); |
There was a problem hiding this comment.
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.jswhere we define our common defaults....loadingMessage, searchMessage, etc. If all power selects arerenderInPlace=true, then this would be a great place to do it.There was a problem hiding this comment.
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.