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);