diff --git a/app/assets/javascripts/components/story/ExpandedStory/ExpandedStoryNotes.js b/app/assets/javascripts/components/story/ExpandedStory/ExpandedStoryNotes.js index e7a1abcb7..58364c7cb 100644 --- a/app/assets/javascripts/components/story/ExpandedStory/ExpandedStoryNotes.js +++ b/app/assets/javascripts/components/story/ExpandedStory/ExpandedStoryNotes.js @@ -25,6 +25,10 @@ class ExpandedStoryNotes extends React.Component { this.setState({ value: '' }); } + hasAnEmptyValue() { + return !this.state.value.trim() + } + notesForm() { return (
@@ -39,6 +43,7 @@ class ExpandedStoryNotes extends React.Component { type='button' value={I18n.t('add note')} onClick={this.handleSave} + disabled={this.hasAnEmptyValue()} />
diff --git a/app/assets/javascripts/components/story/ExpandedStory/ExpandedStoryTask.js b/app/assets/javascripts/components/story/ExpandedStory/ExpandedStoryTask.js index 885c6e0d6..80d8934aa 100644 --- a/app/assets/javascripts/components/story/ExpandedStory/ExpandedStoryTask.js +++ b/app/assets/javascripts/components/story/ExpandedStory/ExpandedStoryTask.js @@ -28,6 +28,10 @@ class ExpandedStoryTask extends Component { }); } + hasAnEmptyValue() { + return !this.state.task.trim() + } + render() { const { story, onToggle, onDelete } = this.props; @@ -57,6 +61,7 @@ class ExpandedStoryTask extends Component { type='submit' className='Story__add-task-button' onClick={this.onHandleSubmit} + disabled={this.hasAnEmptyValue()} > {I18n.t('add task')} diff --git a/app/assets/javascripts/views/story_view.js b/app/assets/javascripts/views/story_view.js index cfcfa53eb..7d9a4a66e 100644 --- a/app/assets/javascripts/views/story_view.js +++ b/app/assets/javascripts/views/story_view.js @@ -784,11 +784,23 @@ module.exports = FormView.extend({ />, $noteForm.get(0) ); - - $noteForm.find('textarea').atwho({ + const addNoteButton = $noteForm.find('button') + const noteTextArea = $noteForm.find('textarea') + + addNoteButton.attr('disabled', 'disabled') + + noteTextArea.atwho({ at: '@', data: window.projectView.usernames() }); + + noteTextArea.keyup(function() { + if ($.trim(noteTextArea.val())) { + addNoteButton.removeAttr('disabled'); + } else { + addNoteButton.attr('disabled', 'disabled'); + } + }); } }, @@ -839,6 +851,18 @@ module.exports = FormView.extend({ />, $taskForm.get(0) ); + const addTaskButton = $taskForm.find('button') + const taskTextArea = $taskForm.find('input') + + addTaskButton.attr('disabled', 'disabled') + + taskTextArea.keyup(function() { + if ($.trim(taskTextArea.val())) { + addTaskButton.removeAttr('disabled'); + } else { + addTaskButton.attr('disabled', 'disabled'); + } + }); } }, diff --git a/spec/javascripts/components/story/expanded_story/expanded_story_notes_spec.js b/spec/javascripts/components/story/expanded_story/expanded_story_notes_spec.js index 50c6e9524..5995d5eeb 100644 --- a/spec/javascripts/components/story/expanded_story/expanded_story_notes_spec.js +++ b/spec/javascripts/components/story/expanded_story/expanded_story_notes_spec.js @@ -42,6 +42,24 @@ describe('', () => { expect(wrapper.find('NotesList')).toExist(); }); + it('disables the add note button if text area is empty', ()=>{ + const story = { notes: [] }; + + const wrapper = shallow( + + ); + + const textArea = wrapper.find('.create-note-text'); + const button = wrapper.find('.create-note-button input'); + + textArea.simulate('change', { target: { value: '' } }); + expect(button.prop('disabled')).toBe(true); + }); + describe('when user create a new note', () => { it('triggers the onCreate callback passing the note', () => { const story = { diff --git a/spec/javascripts/components/story/expanded_story/expanded_story_task_spec.js b/spec/javascripts/components/story/expanded_story/expanded_story_task_spec.js index 391e02ba1..962a1d30a 100644 --- a/spec/javascripts/components/story/expanded_story/expanded_story_task_spec.js +++ b/spec/javascripts/components/story/expanded_story/expanded_story_task_spec.js @@ -26,6 +26,14 @@ describe('', () => { expect(wrapper.text()).toContain(I18n.t('story.tasks')); }); + it('disables the add task button if text area is empty', ()=>{ + const { input } = setup(); + const { button } = setup(); + + input.simulate('change', { target: { value: '' } }); + expect(button.prop('disabled')).toBe(true); + }); + describe('onHandleSubmit', () => { it('calls onSave with a task', () => { const task = 'New Task'; diff --git a/spec/javascripts/views/story_view_spec.js b/spec/javascripts/views/story_view_spec.js index 420548ca4..fd0ed4ebd 100644 --- a/spec/javascripts/views/story_view_spec.js +++ b/spec/javascripts/views/story_view_spec.js @@ -441,6 +441,16 @@ describe('StoryView', function() { expect(this.view.model.notes.last().isNew()).toBeTruthy(); }); + describe("when the text area is empty", function() { + + it("disables the add button", function() { + this.view.canEdit = sinon.stub().returns(true); + this.view.render(); + + expect(this.view.$('.add-note').is(':disabled')).toEqual(true); + }); + }) + it("doesn't add a blank note if the story is new", function() { var stub = sinon.stub(this.view.model, 'isNew'); stub.returns(true); @@ -484,6 +494,13 @@ describe('StoryView', function() { expect(this.view.model.tasks.last().isNew()).toBeTruthy(); }); + it("disables the add button if the input is empty", function() { + this.view.canEdit = sinon.stub().returns(true); + this.view.render(); + + expect(this.view.$('.add-task').is(':disabled')).toEqual(true); + }); + it("doesn't add a blank task if the story is new", function() { var stub = sinon.stub(this.view.model, 'isNew'); stub.returns(true);