diff options
author | Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> | 2015-06-29 08:35:40 +0000 |
---|---|---|
committer | Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> | 2015-06-29 08:35:40 +0000 |
commit | 83d8d611e949135aeb09ff8cc93472644ef6ca98 (patch) | |
tree | 521df40e25fba850c62a0adee51bb456cac7b988 | |
parent | 4642dcd591c32d9838c81472943da2286f4fa9ff (diff) | |
parent | ae75b2ee4099448f89af0fc6eaa9c508f36df5ef (diff) | |
download | gitlab-ce-83d8d611e949135aeb09ff8cc93472644ef6ca98.tar.gz |
Merge branch 'rs-disables-submit-behavior' into 'master'
Add "Requires Input" JS behavior
This aims to replace all of those `disableButtonIfEmptyField` calls littered throughout views and JS with a simple, clean, markup-based behavior.
While going through all of the old usages, I found a few places where the old behavior wasn't actually working, so... bonus, they work now!
See merge request !900
23 files changed, 131 insertions, 55 deletions
diff --git a/app/assets/javascripts/behaviors/requires_input.js.coffee b/app/assets/javascripts/behaviors/requires_input.js.coffee new file mode 100644 index 00000000000..8318fe435b3 --- /dev/null +++ b/app/assets/javascripts/behaviors/requires_input.js.coffee @@ -0,0 +1,39 @@ +# Requires Input behavior +# +# When called on a form with input fields with the `required` attribute, the +# form's submit button will be disabled until all required fields have values. +# +#= require extensions/jquery +# +# ### Example Markup +# +# <form class="js-requires-input"> +# <input type="text" required="required"> +# <input type="submit" value="Submit"> +# </form> +# +$.fn.requiresInput = -> + $form = $(this) + $button = $('button[type=submit], input[type=submit]', $form) + + required = '[required=required]' + fieldSelector = "input#{required}, select#{required}, textarea#{required}" + + requireInput = -> + # Collect the input values of *all* required fields + values = _.map $(fieldSelector, $form), (field) -> field.value + + # Disable the button if any required fields are empty + if values.length && _.any(values, _.isEmpty) + $button.disable() + else + $button.enable() + + # Set initial button state + requireInput() + + $form.on 'change input', fieldSelector, requireInput + +# Triggered on standard document `ready` and on Turbolinks `page:load` events +$(document).on 'ready page:load', -> + $('form.js-requires-input').requiresInput() diff --git a/app/assets/javascripts/blob/edit_blob.js.coffee b/app/assets/javascripts/blob/edit_blob.js.coffee index 2e91a06daa8..050888f9c15 100644 --- a/app/assets/javascripts/blob/edit_blob.js.coffee +++ b/app/assets/javascripts/blob/edit_blob.js.coffee @@ -11,7 +11,6 @@ class @EditBlob if ace_mode editor.getSession().setMode "ace/mode/" + ace_mode - disableButtonIfEmptyField "#commit_message", ".js-commit-button" $(".js-commit-button").click -> $("#file-content").val editor.getValue() $(".file-editor form").submit() diff --git a/app/assets/javascripts/blob/new_blob.js.coffee b/app/assets/javascripts/blob/new_blob.js.coffee index ab8f98715e8..1f36a53f191 100644 --- a/app/assets/javascripts/blob/new_blob.js.coffee +++ b/app/assets/javascripts/blob/new_blob.js.coffee @@ -11,7 +11,6 @@ class @NewBlob if ace_mode editor.getSession().setMode "ace/mode/" + ace_mode - disableButtonIfEmptyField "#commit_message", ".js-commit-button" $(".js-commit-button").click -> $("#file-content").val editor.getValue() $(".file-editor form").submit() diff --git a/app/assets/javascripts/labels.js.coffee b/app/assets/javascripts/labels.js.coffee index 1bc8840f9ac..d05bacd7494 100644 --- a/app/assets/javascripts/labels.js.coffee +++ b/app/assets/javascripts/labels.js.coffee @@ -1,7 +1,6 @@ class @Labels constructor: -> form = $('.label-form') - @setupLabelForm(form) @cleanBinding() @addBinding() @updateColorPreview() @@ -14,10 +13,6 @@ class @Labels $(document).off 'click', '.suggest-colors a' $(document).off 'input', 'input#label_color' - # Initializes the form to disable the save button if no color or title is entered - setupLabelForm: (form) -> - disableButtonIfAnyEmptyField form, '.form-control', form.find('.js-save-button') - # Updates the the preview color with the hex-color input updateColorPreview: => previewColor = $('input#label_color').val() diff --git a/app/assets/javascripts/project_new.js.coffee b/app/assets/javascripts/project_new.js.coffee index 836269c44f9..fecdb9fc2e7 100644 --- a/app/assets/javascripts/project_new.js.coffee +++ b/app/assets/javascripts/project_new.js.coffee @@ -3,9 +3,3 @@ class @ProjectNew $('.project-edit-container').on 'ajax:before', => $('.project-edit-container').hide() $('.save-project-loader').show() - - @initEvents() - - - initEvents: -> - disableButtonIfEmptyField '#project_name', '.project-submit' diff --git a/app/views/admin/identities/_form.html.haml b/app/views/admin/identities/_form.html.haml index b405aa6e8e3..0525552ebf8 100644 --- a/app/views/admin/identities/_form.html.haml +++ b/app/views/admin/identities/_form.html.haml @@ -12,7 +12,7 @@ .form-group = f.label :extern_uid, "Identifier", class: 'control-label' .col-sm-10 - = f.text_field :extern_uid, required: true, class: 'form-control', required: true + = f.text_field :extern_uid, class: 'form-control', required: true .form-actions = f.submit 'Save changes', class: "btn btn-save" diff --git a/app/views/projects/blob/_remove.html.haml b/app/views/projects/blob/_remove.html.haml index b8d8451880a..cae5ff01099 100644 --- a/app/views/projects/blob/_remove.html.haml +++ b/app/views/projects/blob/_remove.html.haml @@ -9,13 +9,10 @@ %strong= @ref .modal-body - = form_tag namespace_project_blob_path(@project.namespace, @project, @id), method: :delete, class: 'form-horizontal' do + = form_tag namespace_project_blob_path(@project.namespace, @project, @id), method: :delete, class: 'form-horizontal js-requires-input' do = render 'shared/commit_message_container', params: params, placeholder: 'Removed this file because...' .form-group .col-sm-offset-2.col-sm-10 = button_tag 'Remove file', class: 'btn btn-remove btn-remove-file' = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" - -:javascript - disableButtonIfEmptyField('#commit_message', '.btn-remove-file') diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml index e78181f8801..a12cd660fc1 100644 --- a/app/views/projects/blob/edit.html.haml +++ b/app/views/projects/blob/edit.html.haml @@ -11,10 +11,9 @@ %i.fa.fa-eye = editing_preview_title(@blob.name) - = form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: "form-horizontal") do + = form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'form-horizontal js-requires-input') do = render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data - = render 'shared/commit_message_container', params: params, - placeholder: "Update #{@blob.name}" + = render 'shared/commit_message_container', params: params, placeholder: "Update #{@blob.name}" .form-group.branch = label_tag 'branch', class: 'control-label' do @@ -25,8 +24,7 @@ = hidden_field_tag 'last_commit', @last_commit = hidden_field_tag 'content', '', id: "file-content" = hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id] - = render 'projects/commit_button', ref: @ref, - cancel_path: @after_edit_path + = render 'projects/commit_button', ref: @ref, cancel_path: @after_edit_path :javascript blob = new EditBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", "#{@blob.language.try(:ace_mode)}") diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index f7ddf74b4fc..7c2a4fece94 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -1,7 +1,7 @@ - page_title "New File", @ref %h3.page-title New file .file-editor - = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal form-new-file') do + = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal form-new-file js-requires-input') do = render 'projects/blob/editor', ref: @ref = render 'shared/commit_message_container', params: params, placeholder: 'Add new file' diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index cac5dc91afd..29e82b93883 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -6,7 +6,7 @@ %h3.page-title %i.fa.fa-code-fork New branch -= form_tag namespace_project_branches_path, method: :post, id: "new-branch-form", class: "form-horizontal" do += form_tag namespace_project_branches_path, method: :post, id: "new-branch-form", class: "form-horizontal js-requires-input" do .form-group = label_tag :branch_name, 'Name for new branch', class: 'control-label' .col-sm-10 @@ -20,7 +20,6 @@ = link_to 'Cancel', namespace_project_branches_path(@project.namespace, @project), class: 'btn btn-cancel' :javascript - disableButtonIfAnyEmptyField($("#new-branch-form"), ".form-control", ".btn-create"); var availableTags = #{@project.repository.ref_names.to_json}; $("#ref").autocomplete({ diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml index a0e904cfd8b..3019893d12c 100644 --- a/app/views/projects/compare/_form.html.haml +++ b/app/views/projects/compare/_form.html.haml @@ -1,16 +1,16 @@ -= form_tag namespace_project_compare_index_path(@project.namespace, @project), method: :post, class: 'form-inline' do += form_tag namespace_project_compare_index_path(@project.namespace, @project), method: :post, class: 'form-inline js-requires-input' do .clearfix.append-bottom-20 - if params[:to] && params[:from] = link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has_tooltip', title: 'Switch base of comparison'} .form-group .input-group.inline-input-group %span.input-group-addon from - = text_field_tag :from, params[:from], class: "form-control" + = text_field_tag :from, params[:from], class: "form-control", required: true = "..." .form-group .input-group.inline-input-group %span.input-group-addon to - = text_field_tag :to, params[:to], class: "form-control" + = text_field_tag :to, params[:to], class: "form-control", required: true = button_tag "Compare", class: "btn btn-create commits-compare-btn" - if create_mr_button? @@ -26,5 +26,3 @@ source: availableTags, minLength: 1 }); - - disableButtonIfEmptyField('#to', '.commits-compare-btn'); diff --git a/app/views/projects/labels/_form.html.haml b/app/views/projects/labels/_form.html.haml index d791ed3410c..534c545329b 100644 --- a/app/views/projects/labels/_form.html.haml +++ b/app/views/projects/labels/_form.html.haml @@ -1,4 +1,4 @@ -= form_for [@project.namespace.becomes(Namespace), @project, @label], html: { class: 'form-horizontal label-form' } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @label], html: { class: 'form-horizontal label-form js-requires-input' } do |f| -if @label.errors.any? .row .col-sm-offset-2.col-sm-10 diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml index 8f225a432e4..9cf389dbe38 100644 --- a/app/views/projects/merge_requests/_form.html.haml +++ b/app/views/projects/merge_requests/_form.html.haml @@ -1,9 +1,8 @@ -= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form' } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form js-requires-input' } do |f| .merge-request-form-info = render 'shared/issuable/form', f: f, issuable: @merge_request :javascript - disableButtonIfEmptyField("#merge_request_title", ".btn-save"); $('.assign-to-me-link').on('click', function(e){ $('#merge_request_assignee_id').val("#{current_user.id}").trigger("change"); e.preventDefault(); diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml index e611b23bca6..ff9c0cdb283 100644 --- a/app/views/projects/merge_requests/_new_compare.html.haml +++ b/app/views/projects/merge_requests/_new_compare.html.haml @@ -1,6 +1,6 @@ %p.lead Compare branches for new Merge Request -= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], url: new_namespace_project_merge_request_path(@project.namespace, @project), method: :get, html: { class: "merge-request-form form-inline" } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @merge_request], url: new_namespace_project_merge_request_path(@project.namespace, @project), method: :get, html: { class: "merge-request-form form-inline js-requires-input" } do |f| .hide.alert.alert-danger.mr-compare-errors .merge-request-branches.row .col-md-6 @@ -8,9 +8,9 @@ .panel-heading %strong Source branch .panel-body - = f.select(:source_project_id, [[@merge_request.source_project_path,@merge_request.source_project.id]] , {}, { class: 'source_project select2 span3', disabled: @merge_request.persisted? }) + = f.select(:source_project_id, [[@merge_request.source_project_path,@merge_request.source_project.id]] , {}, { class: 'source_project select2 span3', disabled: @merge_request.persisted?, required: true }) - = f.select(:source_branch, @merge_request.source_branches, { include_blank: "Select branch" }, {class: 'source_branch select2 span2'}) + = f.select(:source_branch, @merge_request.source_branches, { include_blank: "Select branch" }, {class: 'source_branch select2 span2', required: true}) .panel-footer .mr_source_commit @@ -20,9 +20,9 @@ %strong Target branch .panel-body - projects = @project.forked_from_project.nil? ? [@project] : [@project, @project.forked_from_project] - = f.select(:target_project_id, options_from_collection_for_select(projects, 'id', 'path_with_namespace', f.object.target_project_id), {}, { class: 'target_project select2 span3', disabled: @merge_request.persisted? }) + = f.select(:target_project_id, options_from_collection_for_select(projects, 'id', 'path_with_namespace', f.object.target_project_id), {}, { class: 'target_project select2 span3', disabled: @merge_request.persisted?, required: true }) - = f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, {class: 'target_branch select2 span2'}) + = f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, {class: 'target_branch select2 span2', required: true}) .panel-footer .mr_target_commit diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index 2f147f9095d..633a54f3620 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -9,7 +9,7 @@ %span.pull-right = link_to 'Change branches', mr_change_branches_path(@merge_request) %hr -= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form' } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form js-requires-input' } do |f| .merge-request-form-info = render 'shared/issuable/form', f: f, issuable: @merge_request = f.hidden_field :source_project_id diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml index 41aa66dd76b..f5bacaf280a 100644 --- a/app/views/projects/merge_requests/widget/open/_accept.html.haml +++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml @@ -1,4 +1,4 @@ -= form_for [:automerge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form' } do |f| += form_for [:automerge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-requires-input' } do |f| = hidden_field_tag :authenticity_token, form_authenticity_token .accept-merge-holder.clearfix.js-toggle-container .accept-action @@ -25,8 +25,6 @@ = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" :coffeescript - disableButtonIfEmptyField '#commit_message', '.accept_merge_request' - $('.accept-mr-form').on 'ajax:before', -> btn = $('.accept_merge_request') btn.disable() diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml index 5650607f31f..b93462e5bdf 100644 --- a/app/views/projects/milestones/_form.html.haml +++ b/app/views/projects/milestones/_form.html.haml @@ -5,7 +5,7 @@ %hr -= form_for [@project.namespace.becomes(Namespace), @project, @milestone], html: {class: 'form-horizontal milestone-form gfm-form'} do |f| += form_for [@project.namespace.becomes(Namespace), @project, @milestone], html: {class: 'form-horizontal milestone-form gfm-form js-requires-input'} do |f| -if @milestone.errors.any? .alert.alert-danger %ul @@ -16,7 +16,7 @@ .form-group = f.label :title, "Title", class: "control-label" .col-sm-10 - = f.text_field :title, maxlength: 255, class: "form-control" + = f.text_field :title, maxlength: 255, class: "form-control", required: true %p.hint Required .form-group.milestone-description = f.label :description, "Description", class: "control-label" @@ -45,7 +45,6 @@ :javascript - disableButtonIfEmptyField("#milestone_title", ".btn-save"); $( ".datepicker" ).datepicker({ dateFormat: "yy-mm-dd", onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) } diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml index c67a7d256a8..a88cf167511 100644 --- a/app/views/projects/network/show.html.haml +++ b/app/views/projects/network/show.html.haml @@ -4,8 +4,8 @@ .controls = form_tag namespace_project_network_path(@project.namespace, @project, @id), method: :get, class: 'form-inline network-form' do |f| = text_field_tag :extended_sha1, @options[:extended_sha1], placeholder: "Input an extended SHA1 syntax", class: 'search-input form-control input-mx-250 search-sha' - = button_tag class: 'btn btn-success btn-search-sha' do - %i.fa.fa-search + = button_tag class: 'btn btn-success' do + = icon('search') .inline.prepend-left-20 .checkbox.light = label_tag :filter_ref do @@ -16,8 +16,6 @@ = spinner nil, true :javascript - disableButtonIfEmptyField('#extended_sha1', '.btn-search-sha') - network_graph = new Network({ url: '#{namespace_project_network_path(@project.namespace, @project, @ref, @options.merge(format: :json))}', commit_url: '#{namespace_project_commit_path(@project.namespace, @project, 'ae45ca32').gsub("ae45ca32", "%s")}', diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index e56d8615132..5114e63c05f 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -5,13 +5,13 @@ = render 'projects/errors' .project-edit-content - = form_for @project, html: { class: 'new_project form-horizontal' } do |f| + = form_for @project, html: { class: 'new_project form-horizontal js-requires-input' } do |f| .form-group.project-name-holder = f.label :path, class: 'control-label' do %strong Project path .col-sm-10 .input-group - = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 1, autofocus: true + = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 1, autofocus: true, required: true .input-group-addon \.git diff --git a/features/project/forked_merge_requests.feature b/features/project/forked_merge_requests.feature index ad1160e3343..10bd6fec803 100644 --- a/features/project/forked_merge_requests.feature +++ b/features/project/forked_merge_requests.feature @@ -26,7 +26,6 @@ Feature: Project Forked Merge Requests #And I save the merge request #Then I should see the edited merge request - @javascript Scenario: I cannot submit an invalid merge request Given I visit project "Forked Shop" merge requests page And I click link "New Merge Request" diff --git a/spec/javascripts/behaviors/requires_input_spec.js.coffee b/spec/javascripts/behaviors/requires_input_spec.js.coffee new file mode 100644 index 00000000000..61a17632173 --- /dev/null +++ b/spec/javascripts/behaviors/requires_input_spec.js.coffee @@ -0,0 +1,49 @@ +#= require behaviors/requires_input + +describe 'requiresInput', -> + fixture.preload('behaviors/requires_input.html') + + beforeEach -> + fixture.load('behaviors/requires_input.html') + + it 'disables submit when any field is required', -> + $('.js-requires-input').requiresInput() + + expect($('.submit')).toBeDisabled() + + it 'enables submit when no field is required', -> + $('*[required=required]').removeAttr('required') + + $('.js-requires-input').requiresInput() + + expect($('.submit')).not.toBeDisabled() + + it 'enables submit when all required fields are pre-filled', -> + $('*[required=required]').remove() + + $('.js-requires-input').requiresInput() + + expect($('.submit')).not.toBeDisabled() + + it 'enables submit when all required fields receive input', -> + $('.js-requires-input').requiresInput() + + $('#required1').val('input1').change() + expect($('.submit')).toBeDisabled() + + $('#optional1').val('input1').change() + expect($('.submit')).toBeDisabled() + + $('#required2').val('input2').change() + $('#required3').val('input3').change() + $('#required4').val('input4').change() + $('#required5').val('1').change() + + expect($('.submit')).not.toBeDisabled() + + it 'is called on page:load event', -> + spy = spyOn($.fn, 'requiresInput') + + $(document).trigger('page:load') + + expect(spy).toHaveBeenCalled() diff --git a/spec/javascripts/fixtures/behaviors/requires_input.html.haml b/spec/javascripts/fixtures/behaviors/requires_input.html.haml new file mode 100644 index 00000000000..c3f905e912e --- /dev/null +++ b/spec/javascripts/fixtures/behaviors/requires_input.html.haml @@ -0,0 +1,18 @@ +%form.js-requires-input + %input{type: 'text', id: 'required1', required: 'required'} + %input{type: 'text', id: 'required2', required: 'required'} + %input{type: 'text', id: 'required3', required: 'required', value: 'Pre-filled'} + %input{type: 'text', id: 'optional1'} + + %textarea{id: 'required4', required: 'required'} + %textarea{id: 'optional2'} + + %select{id: 'required5', required: 'required'} + %option Zero + %option{value: '1'} One + %select{id: 'optional3', required: 'required'} + %option Zero + %option{value: '1'} One + + %button.submit{type: 'submit', value: 'Submit'} + %input.submit{type: 'submit', value: 'Submit'} diff --git a/spec/javascripts/merge_request_spec.js.coffee b/spec/javascripts/merge_request_spec.js.coffee index a4735af0343..22ebc7039d1 100644 --- a/spec/javascripts/merge_request_spec.js.coffee +++ b/spec/javascripts/merge_request_spec.js.coffee @@ -1,7 +1,5 @@ #= require merge_request -window.disableButtonIfEmptyField = -> null - describe 'MergeRequest', -> describe 'task lists', -> fixture.preload('merge_requests_show.html') |