[PATCH] Enable buttons only when all required data is entered

Most forms contain fields that must be filled before they are submitted. Some of them submit the missing data and display an error message when something goes wrong, and some of them do not allow the user to submit them if some data is missing. Make the forms behavior consistent by enabling their buttons only when the required data is entered. Internally, the page tags all required fields with the class "required" and whenever any of their values change, they are checke. If any required field is empty, the submit button becomses disabled; otherwise, it becomes enabled. Fix GitHub issue #317 ("Inconsistent button status when adding or creating new resources"). Signed-off-by: Crístian Viana <vianac@linux.vnet.ibm.com> --- ui/css/theme-default/login-window.css | 4 -- ui/js/src/kimchi.guest_edit_main.js | 15 +++++++ ui/js/src/kimchi.login_window.js | 36 +++++++-------- ui/js/src/kimchi.storagepool_add_main.js | 75 +++++++++++++++++++++++--------- ui/js/src/kimchi.template_edit_main.js | 20 +++++++++ ui/pages/guest-edit.html.tmpl | 6 +-- ui/pages/i18n.html.tmpl | 6 --- ui/pages/login-window.html.tmpl | 8 ++-- ui/pages/storagepool-add.html.tmpl | 6 +-- ui/pages/template-edit.html.tmpl | 8 ++-- 10 files changed, 117 insertions(+), 67 deletions(-) diff --git a/ui/css/theme-default/login-window.css b/ui/css/theme-default/login-window.css index 8a21090..d021be6 100644 --- a/ui/css/theme-default/login-window.css +++ b/ui/css/theme-default/login-window.css @@ -62,10 +62,6 @@ width: 290px; } -#login-window .login-panel .msg-required { - color: red; -} - #login-window .login-panel button { font-size: 18px; height: 40px; diff --git a/ui/js/src/kimchi.guest_edit_main.js b/ui/js/src/kimchi.guest_edit_main.js index 0236e2d..bb4ee51 100644 --- a/ui/js/src/kimchi.guest_edit_main.js +++ b/ui/js/src/kimchi.guest_edit_main.js @@ -202,6 +202,21 @@ kimchi.guest_edit_main = function() { kimchi.retrieveVM(kimchi.selectedGuest, initContent); + var updateSubmitButtonStatus = function() { + var valid = true; + guestEditForm.find('input.required').each(function(index, field) { + if (field.value === '') { + valid = false; + return; + } + }); + + saveButton.prop('disabled', !valid); + }; + + guestEditForm.find('input.required').on('input propertychange', + updateSubmitButtonStatus); + var submitForm = function(event) { $(saveButton).prop('disabled', true); var data=$('#form-guest-edit-general').serializeObject(); diff --git a/ui/js/src/kimchi.login_window.js b/ui/js/src/kimchi.login_window.js index 9c06a50..52394a5 100644 --- a/ui/js/src/kimchi.login_window.js +++ b/ui/js/src/kimchi.login_window.js @@ -30,26 +30,25 @@ kimchi.login_main = function() { $(opt).prop('selected', selectedLanguage === k); } + var updateSubmitButtonStatus = function() { + var valid = true; + $('#form-login').find('input.required').each(function(index, field) { + if (field.value === '') { + valid = false; + return; + } + }); + + $('#btn-login').prop('disabled', !valid); + } + + $('#form-login').find('input.required').on('input propertychange', + updateSubmitButtonStatus); + $('#language').on('change', function() { kimchi.topic('languageChanged').publish($(this).val()); }); - var validateNonEmpty = function(idsArray) { - for(var i = 0; i < idsArray.length; i++) { - var id = idsArray[i]; - if (!$('#' + id).val()) { - $('#' + id + '-msg').text(i18n['KCHAUTH6002E']); - placeCursor(id); - return false; - } - else { - $('#' + id + '-msg').empty(); - } - } - - return true; - }; - var placeCursor = function(id) { if (id && $('#' + id).size() > 0) { $('#' + id).focus(); @@ -66,11 +65,6 @@ kimchi.login_main = function() { }; var login = function(event) { - - if (!validateNonEmpty(['username', 'password'])) { - return false; - } - $('#btn-login').text(i18n['KCHAUTH6002M']).prop('disabled', true); var userName = $('#username').val(); diff --git a/ui/js/src/kimchi.storagepool_add_main.js b/ui/js/src/kimchi.storagepool_add_main.js index 86dbe7f..5356985 100644 --- a/ui/js/src/kimchi.storagepool_add_main.js +++ b/ui/js/src/kimchi.storagepool_add_main.js @@ -122,6 +122,18 @@ kimchi.initStorageAddPage = function() { }); }); + var updateSubmitButtonStatus = function() { + var valid = true; + $('#form-pool-add').find('input.required').each(function(index, field) { + if (field.value === '') { + valid = false; + return; + } + }); + + $('#pool-doAdd').prop('disabled', !valid); + }; + $('#poolTypeInputId').change(function() { var poolObject = {'dir': ".path-section", 'netfs': '.nfs-section', 'iscsi': '.iscsi-section', 'scsi': '.scsi-section', @@ -134,7 +146,49 @@ kimchi.initStorageAddPage = function() { $(value).addClass('tmpl-html'); } }); + + switch (selectType) { + case 'dir': + $('#pathId').addClass('required'); + + $('#nfsserverId').removeClass('required'); + $('#nfspathId').removeClass('required'); + $('#iscsiserverId').removeClass('required'); + break; + case 'netfs': + $('#nfsserverId').addClass('required'); + $('#nfspathId').addClass('required'); + + $('#pathId').removeClass('required'); + $('#iscsiserverId').removeClass('required'); + break; + case 'iscsi': + $('#iscsiserverId').addClass('required'); + + $('#pathId').removeClass('required'); + $('#nfsserverId').removeClass('required'); + $('#nfspathId').removeClass('required'); + break; + case 'scsi': + $('#pathId').removeClass('required'); + $('#nfsserverId').removeClass('required'); + $('#nfspathId').removeClass('required'); + $('#iscsiserverId').removeClass('required'); + break; + case 'logical': + $('#pathId').removeClass('required'); + $('#nfsserverId').removeClass('required'); + $('#nfspathId').removeClass('required'); + $('#iscsiserverId').removeClass('required'); + break; + } + + updateSubmitButtonStatus(); }); + + $('#form-pool-add').find('input').on('input propertychange', + updateSubmitButtonStatus); + $('#authId').click(function() { if ($(this).prop("checked")) { $('.authenticationfield').removeClass('tmpl-html'); @@ -150,10 +204,6 @@ kimchi.initStorageAddPage = function() { kimchi.validateForm = function() { var name = $('#poolId').val(); var poolType = $("#poolTypeInputId").val(); - if ('' === name) { - kimchi.message.error.code('KCHPOOL6001E'); - return false; - } if (name.indexOf("/")!=-1) { kimchi.message.error.code('KCHPOOL6004E'); return false; @@ -173,10 +223,6 @@ kimchi.validateForm = function() { kimchi.validateDirForm = function () { var path = $('#pathId').val(); - if ('' === path) { - kimchi.message.error.code('KCHPOOL6002E'); - return false; - } if (!/(^\/.*)$/.test(path)) { kimchi.message.error.code('KCHAPI6003E'); return false; @@ -190,10 +236,6 @@ kimchi.validateNfsForm = function () { if (!kimchi.validateServer(nfsserver)) { return false; } - if ('' === nfspath) { - kimchi.message.error.code('KCHPOOL6003E'); - return false; - } if (!/((\/([0-9a-zA-Z-_\.]+)))$/.test(nfspath)) { kimchi.message.error.code('KCHPOOL6005E'); return false; @@ -203,22 +245,13 @@ kimchi.validateNfsForm = function () { kimchi.validateIscsiForm = function() { var iscsiServer = $('#iscsiserverId').val(); - var iscsiTarget = $('#iscsiTargetId').val(); if (!kimchi.validateServer(iscsiServer)) { return false; } - if ('' === iscsiTarget) { - kimchi.message.error.code('KCHPOOL6007E'); - return false; - } return true; }; kimchi.validateServer = function(serverField) { - if ('' === serverField) { - kimchi.message.error.code('KCHPOOL6008E'); - return false; - } if(!kimchi.isServer(serverField)) { kimchi.message.error.code('KCHPOOL6009E'); return false; diff --git a/ui/js/src/kimchi.template_edit_main.js b/ui/js/src/kimchi.template_edit_main.js index f0f4718..e552bf9 100644 --- a/ui/js/src/kimchi.template_edit_main.js +++ b/ui/js/src/kimchi.template_edit_main.js @@ -104,6 +104,26 @@ kimchi.template_edit_main = function() { }); }); + var updateSubmitButtonStatus = function() { + var valid = true; + templateEditForm.find('input.required').each(function(index, field) { + if (field.value === '') { + valid = false; + return; + } + }); + + if (valid) { + $('#tmpl-edit-button-save').removeAttr('disabled'); + } + else { + $('#tmpl-edit-button-save').attr('disabled', 'disabled'); + } + }; + + templateEditForm.find('input.required').on('input propertychange', + updateSubmitButtonStatus); + $('#tmpl-edit-button-cancel').on('click', function() { kimchi.window.close(); }); diff --git a/ui/pages/guest-edit.html.tmpl b/ui/pages/guest-edit.html.tmpl index 96d907e..d35ad7a 100644 --- a/ui/pages/guest-edit.html.tmpl +++ b/ui/pages/guest-edit.html.tmpl @@ -49,7 +49,7 @@ </div> <div class="guest-edit-wrapper-controls"> <input id="guest-edit-id-textbox" - name="name" type="text" /> + name="name" type="text" class="required" /> </div> </div> <div> @@ -62,7 +62,7 @@ <input id="guest-edit-cores-textbox" name="cpus" - type="text" /> + type="text" class="required" /> </div> </div> <div> @@ -74,7 +74,7 @@ <div class="guest-edit-wrapper-controls"> <input id="guest-edit-memory-textbox" name="memory" - type="text" /> + type="text" class="required" /> </div> </div> <div> diff --git a/ui/pages/i18n.html.tmpl b/ui/pages/i18n.html.tmpl index 98da828..814825f 100644 --- a/ui/pages/i18n.html.tmpl +++ b/ui/pages/i18n.html.tmpl @@ -33,7 +33,6 @@ <script> var i18n = { 'KCHAUTH6001E': "$_("The username or password you entered is incorrect. Please try again.")", - 'KCHAUTH6002E': "$_("This field is required.")", 'KCHAUTH6001M': "$_("Log in")", 'KCHAUTH6002M': "$_("Logging in...")", @@ -158,14 +157,9 @@ var i18n = { 'KCHPOOL6004M': "$_("SCSI Fibre Channel")", 'KCHPOOL6005M': "$_("No SCSI adapters found.")", - 'KCHPOOL6001E': "$_("The storage pool name can not be blank.")", - 'KCHPOOL6002E': "$_("The storage pool path can not be blank.")", - 'KCHPOOL6003E': "$_("NFS server mount path can not be blank.")", 'KCHPOOL6004E': "$_("Invalid storage pool name. It should not contain '/'.")", 'KCHPOOL6005E': "$_("Invalid NFS mount path.")", 'KCHPOOL6006E': "$_("No logical device selected.")", - 'KCHPOOL6007E': "$_("The iSCSI target can not be blank.")", - 'KCHPOOL6008E': "$_("Server name can not be blank.")", 'KCHPOOL6009E': "$_("This is not a valid Server Name or IP. please, modify it.")", 'KCHPOOL6010M': "$_("Looking for available partitions ...")", 'KCHPOOL6011M': "$_("No available partitions found.")", diff --git a/ui/pages/login-window.html.tmpl b/ui/pages/login-window.html.tmpl index 3e451c4..1cd9e83 100644 --- a/ui/pages/login-window.html.tmpl +++ b/ui/pages/login-window.html.tmpl @@ -34,15 +34,13 @@ <div class="content login-panel"> <form id="form-login" action="/login" method="POST"> <div class="row"> - <input type="text" id="username" name="username" required="required" placeholder="$_("User Name")" /> - <div id="username-msg" class="msg-required"></div> + <input type="text" id="username" name="username" class="required" placeholder="$_("User Name")" /> </div> <div class="row"> - <input type="password" id="password" name="password" required="required" placeholder="$_("Password")" /> - <div id="password-msg" class="msg-required"></div> + <input type="password" id="password" name="password" class="required" placeholder="$_("Password")" /> </div> <div class="row"> - <button id="btn-login" class="btn-normal">$_("Log in")</button> + <button id="btn-login" class="btn-normal" disabled="true">$_("Log in")</button> </div> </form> </div> diff --git a/ui/pages/storagepool-add.html.tmpl b/ui/pages/storagepool-add.html.tmpl index 977db66..6ce1343 100644 --- a/ui/pages/storagepool-add.html.tmpl +++ b/ui/pages/storagepool-add.html.tmpl @@ -36,7 +36,7 @@ <p class="text-help"> $_("The name used to identify the storage pools, and it should not be empty.") </p> - <input id="poolId" required="required" type="text" class="text storage-base-input-width" name="name"> + <input id="poolId" type="text" class="text storage-base-input-width required" name="name"> </div> </section> <section class="form-section"> @@ -60,7 +60,7 @@ $_("The path of the Storage Pool. Each Storage Pool must have a unique path.")</p> <p class="text-help"> $_("Kimchi will try to create the directory when it does not already exist in your system.")</p> - <input id="pathId" type="text" class="text storage-base-input-width"> + <input id="pathId" type="text" class="text storage-base-input-width required"> </div> <div class="clear"></div> </section> @@ -152,7 +152,7 @@ </div> <footer> <div class="btn-group"> - <button id="pool-doAdd" class="btn-normal"> + <button id="pool-doAdd" class="btn-normal" disabled="disabled"> <span class="text">$_("Create")</span> </button> </div> diff --git a/ui/pages/template-edit.html.tmpl b/ui/pages/template-edit.html.tmpl index 434d938..46b7058 100644 --- a/ui/pages/template-edit.html.tmpl +++ b/ui/pages/template-edit.html.tmpl @@ -36,7 +36,7 @@ <label for="template-edit-id-textbox">$_("Name")</label> </div> <div class="template-edit-wrapper-controls"> - <input id="template-edit-id-textbox" name="name" type="text" /> + <input id="template-edit-id-textbox" name="name" type="text" class="required" /> </div> </div> <div> @@ -60,7 +60,7 @@ <label for="template-edit-cpu-textbox">$_("CPU Number")</label> </div> <div class="template-edit-wrapper-controls"> - <input id="template-edit-cpu-textbox" name="cpus" type="text" /> + <input id="template-edit-cpu-textbox" name="cpus" type="text" class="required" /> </div> </div> <div> @@ -68,7 +68,7 @@ <label for="template-edit-memory-textbox">$_("Memory")</label> </div> <div class="template-edit-wrapper-controls"> - <input id="template-edit-memory-textbox" name="memory" type="text" /> + <input id="template-edit-memory-textbox" name="memory" type="text" class="required" /> </div> </div> <div> @@ -76,7 +76,7 @@ <label for="template-edit-disk-textbox">$_("Disk (GB)")</label> </div> <div class="template-edit-wrapper-controls"> - <input id="template-edit-disk-textbox" name="disks" type="text" /> + <input id="template-edit-disk-textbox" name="disks" type="text" class="required" /> </div> </div> </fieldset> -- 1.9.0

need improve After I apply this patch. my browser remember the user name and password. But I can no login. I can login after I input any character and then delete it. The storage add page is right. Your method is right for storage page. On 05/14/2014 04:44 AM, Crístian Viana wrote:
Most forms contain fields that must be filled before they are submitted. Some of them submit the missing data and display an error message when something goes wrong, and some of them do not allow the user to submit them if some data is missing.
Make the forms behavior consistent by enabling their buttons only when the required data is entered. Internally, the page tags all required fields with the class "required" and whenever any of their values change, they are checke. If any required field is empty, the submit button becomses disabled; otherwise, it becomes enabled.
Fix GitHub issue #317 ("Inconsistent button status when adding or creating new resources").
Signed-off-by: Crístian Viana <vianac@linux.vnet.ibm.com> --- ui/css/theme-default/login-window.css | 4 -- ui/js/src/kimchi.guest_edit_main.js | 15 +++++++ ui/js/src/kimchi.login_window.js | 36 +++++++-------- ui/js/src/kimchi.storagepool_add_main.js | 75 +++++++++++++++++++++++--------- ui/js/src/kimchi.template_edit_main.js | 20 +++++++++ ui/pages/guest-edit.html.tmpl | 6 +-- ui/pages/i18n.html.tmpl | 6 --- ui/pages/login-window.html.tmpl | 8 ++-- ui/pages/storagepool-add.html.tmpl | 6 +-- ui/pages/template-edit.html.tmpl | 8 ++-- 10 files changed, 117 insertions(+), 67 deletions(-)
diff --git a/ui/css/theme-default/login-window.css b/ui/css/theme-default/login-window.css index 8a21090..d021be6 100644 --- a/ui/css/theme-default/login-window.css +++ b/ui/css/theme-default/login-window.css @@ -62,10 +62,6 @@ width: 290px; }
-#login-window .login-panel .msg-required { - color: red; -} - #login-window .login-panel button { font-size: 18px; height: 40px; diff --git a/ui/js/src/kimchi.guest_edit_main.js b/ui/js/src/kimchi.guest_edit_main.js index 0236e2d..bb4ee51 100644 --- a/ui/js/src/kimchi.guest_edit_main.js +++ b/ui/js/src/kimchi.guest_edit_main.js @@ -202,6 +202,21 @@ kimchi.guest_edit_main = function() {
kimchi.retrieveVM(kimchi.selectedGuest, initContent);
+ var updateSubmitButtonStatus = function() { + var valid = true; + guestEditForm.find('input.required').each(function(index, field) { + if (field.value === '') { + valid = false; + return; + } + }); + + saveButton.prop('disabled', !valid); + }; + + guestEditForm.find('input.required').on('input propertychange', + updateSubmitButtonStatus); + var submitForm = function(event) { $(saveButton).prop('disabled', true); var data=$('#form-guest-edit-general').serializeObject(); diff --git a/ui/js/src/kimchi.login_window.js b/ui/js/src/kimchi.login_window.js index 9c06a50..52394a5 100644 --- a/ui/js/src/kimchi.login_window.js +++ b/ui/js/src/kimchi.login_window.js @@ -30,26 +30,25 @@ kimchi.login_main = function() { $(opt).prop('selected', selectedLanguage === k); }
+ var updateSubmitButtonStatus = function() { + var valid = true; + $('#form-login').find('input.required').each(function(index, field) { + if (field.value === '') { + valid = false; + return; + } + }); + + $('#btn-login').prop('disabled', !valid); + } + + $('#form-login').find('input.required').on('input propertychange', + updateSubmitButtonStatus); + $('#language').on('change', function() { kimchi.topic('languageChanged').publish($(this).val()); });
- var validateNonEmpty = function(idsArray) { - for(var i = 0; i < idsArray.length; i++) { - var id = idsArray[i]; - if (!$('#' + id).val()) { - $('#' + id + '-msg').text(i18n['KCHAUTH6002E']); - placeCursor(id); - return false; - } - else { - $('#' + id + '-msg').empty(); - } - } - - return true; - }; - var placeCursor = function(id) { if (id && $('#' + id).size() > 0) { $('#' + id).focus(); @@ -66,11 +65,6 @@ kimchi.login_main = function() { };
var login = function(event) { - - if (!validateNonEmpty(['username', 'password'])) { - return false; - } - $('#btn-login').text(i18n['KCHAUTH6002M']).prop('disabled', true);
var userName = $('#username').val(); diff --git a/ui/js/src/kimchi.storagepool_add_main.js b/ui/js/src/kimchi.storagepool_add_main.js index 86dbe7f..5356985 100644 --- a/ui/js/src/kimchi.storagepool_add_main.js +++ b/ui/js/src/kimchi.storagepool_add_main.js @@ -122,6 +122,18 @@ kimchi.initStorageAddPage = function() { }); });
+ var updateSubmitButtonStatus = function() { + var valid = true; + $('#form-pool-add').find('input.required').each(function(index, field) { + if (field.value === '') { + valid = false; + return; + } + }); + + $('#pool-doAdd').prop('disabled', !valid); + }; + $('#poolTypeInputId').change(function() { var poolObject = {'dir': ".path-section", 'netfs': '.nfs-section', 'iscsi': '.iscsi-section', 'scsi': '.scsi-section', @@ -134,7 +146,49 @@ kimchi.initStorageAddPage = function() { $(value).addClass('tmpl-html'); } }); + + switch (selectType) { + case 'dir': + $('#pathId').addClass('required'); + + $('#nfsserverId').removeClass('required'); + $('#nfspathId').removeClass('required'); + $('#iscsiserverId').removeClass('required'); + break; + case 'netfs': + $('#nfsserverId').addClass('required'); + $('#nfspathId').addClass('required'); + + $('#pathId').removeClass('required'); + $('#iscsiserverId').removeClass('required'); + break; + case 'iscsi': + $('#iscsiserverId').addClass('required'); + + $('#pathId').removeClass('required'); + $('#nfsserverId').removeClass('required'); + $('#nfspathId').removeClass('required'); + break; + case 'scsi': + $('#pathId').removeClass('required'); + $('#nfsserverId').removeClass('required'); + $('#nfspathId').removeClass('required'); + $('#iscsiserverId').removeClass('required'); + break; + case 'logical': + $('#pathId').removeClass('required'); + $('#nfsserverId').removeClass('required'); + $('#nfspathId').removeClass('required'); + $('#iscsiserverId').removeClass('required'); + break; + } + + updateSubmitButtonStatus(); }); + + $('#form-pool-add').find('input').on('input propertychange', + updateSubmitButtonStatus); + $('#authId').click(function() { if ($(this).prop("checked")) { $('.authenticationfield').removeClass('tmpl-html'); @@ -150,10 +204,6 @@ kimchi.initStorageAddPage = function() { kimchi.validateForm = function() { var name = $('#poolId').val(); var poolType = $("#poolTypeInputId").val(); - if ('' === name) { - kimchi.message.error.code('KCHPOOL6001E'); - return false; - } if (name.indexOf("/")!=-1) { kimchi.message.error.code('KCHPOOL6004E'); return false; @@ -173,10 +223,6 @@ kimchi.validateForm = function() {
kimchi.validateDirForm = function () { var path = $('#pathId').val(); - if ('' === path) { - kimchi.message.error.code('KCHPOOL6002E'); - return false; - } if (!/(^\/.*)$/.test(path)) { kimchi.message.error.code('KCHAPI6003E'); return false; @@ -190,10 +236,6 @@ kimchi.validateNfsForm = function () { if (!kimchi.validateServer(nfsserver)) { return false; } - if ('' === nfspath) { - kimchi.message.error.code('KCHPOOL6003E'); - return false; - } if (!/((\/([0-9a-zA-Z-_\.]+)))$/.test(nfspath)) { kimchi.message.error.code('KCHPOOL6005E'); return false; @@ -203,22 +245,13 @@ kimchi.validateNfsForm = function () {
kimchi.validateIscsiForm = function() { var iscsiServer = $('#iscsiserverId').val(); - var iscsiTarget = $('#iscsiTargetId').val(); if (!kimchi.validateServer(iscsiServer)) { return false; } - if ('' === iscsiTarget) { - kimchi.message.error.code('KCHPOOL6007E'); - return false; - } return true; };
kimchi.validateServer = function(serverField) { - if ('' === serverField) { - kimchi.message.error.code('KCHPOOL6008E'); - return false; - } if(!kimchi.isServer(serverField)) { kimchi.message.error.code('KCHPOOL6009E'); return false; diff --git a/ui/js/src/kimchi.template_edit_main.js b/ui/js/src/kimchi.template_edit_main.js index f0f4718..e552bf9 100644 --- a/ui/js/src/kimchi.template_edit_main.js +++ b/ui/js/src/kimchi.template_edit_main.js @@ -104,6 +104,26 @@ kimchi.template_edit_main = function() { }); });
+ var updateSubmitButtonStatus = function() { + var valid = true; + templateEditForm.find('input.required').each(function(index, field) { + if (field.value === '') { + valid = false; + return; + } + }); + + if (valid) { + $('#tmpl-edit-button-save').removeAttr('disabled'); + } + else { + $('#tmpl-edit-button-save').attr('disabled', 'disabled'); + } + }; + + templateEditForm.find('input.required').on('input propertychange', + updateSubmitButtonStatus); + $('#tmpl-edit-button-cancel').on('click', function() { kimchi.window.close(); }); diff --git a/ui/pages/guest-edit.html.tmpl b/ui/pages/guest-edit.html.tmpl index 96d907e..d35ad7a 100644 --- a/ui/pages/guest-edit.html.tmpl +++ b/ui/pages/guest-edit.html.tmpl @@ -49,7 +49,7 @@ </div> <div class="guest-edit-wrapper-controls"> <input id="guest-edit-id-textbox" - name="name" type="text" /> + name="name" type="text" class="required" /> </div> </div> <div> @@ -62,7 +62,7 @@ <input id="guest-edit-cores-textbox" name="cpus" - type="text" /> + type="text" class="required" /> </div> </div> <div> @@ -74,7 +74,7 @@ <div class="guest-edit-wrapper-controls"> <input id="guest-edit-memory-textbox" name="memory" - type="text" /> + type="text" class="required" /> </div> </div> <div> diff --git a/ui/pages/i18n.html.tmpl b/ui/pages/i18n.html.tmpl index 98da828..814825f 100644 --- a/ui/pages/i18n.html.tmpl +++ b/ui/pages/i18n.html.tmpl @@ -33,7 +33,6 @@ <script> var i18n = { 'KCHAUTH6001E': "$_("The username or password you entered is incorrect. Please try again.")", - 'KCHAUTH6002E': "$_("This field is required.")",
'KCHAUTH6001M': "$_("Log in")", 'KCHAUTH6002M': "$_("Logging in...")", @@ -158,14 +157,9 @@ var i18n = { 'KCHPOOL6004M': "$_("SCSI Fibre Channel")", 'KCHPOOL6005M': "$_("No SCSI adapters found.")",
- 'KCHPOOL6001E': "$_("The storage pool name can not be blank.")", - 'KCHPOOL6002E': "$_("The storage pool path can not be blank.")", - 'KCHPOOL6003E': "$_("NFS server mount path can not be blank.")", 'KCHPOOL6004E': "$_("Invalid storage pool name. It should not contain '/'.")", 'KCHPOOL6005E': "$_("Invalid NFS mount path.")", 'KCHPOOL6006E': "$_("No logical device selected.")", - 'KCHPOOL6007E': "$_("The iSCSI target can not be blank.")", - 'KCHPOOL6008E': "$_("Server name can not be blank.")", 'KCHPOOL6009E': "$_("This is not a valid Server Name or IP. please, modify it.")", 'KCHPOOL6010M': "$_("Looking for available partitions ...")", 'KCHPOOL6011M': "$_("No available partitions found.")", diff --git a/ui/pages/login-window.html.tmpl b/ui/pages/login-window.html.tmpl index 3e451c4..1cd9e83 100644 --- a/ui/pages/login-window.html.tmpl +++ b/ui/pages/login-window.html.tmpl @@ -34,15 +34,13 @@ <div class="content login-panel"> <form id="form-login" action="/login" method="POST"> <div class="row"> - <input type="text" id="username" name="username" required="required" placeholder="$_("User Name")" /> - <div id="username-msg" class="msg-required"></div> + <input type="text" id="username" name="username" class="required" placeholder="$_("User Name")" /> </div> <div class="row"> - <input type="password" id="password" name="password" required="required" placeholder="$_("Password")" /> - <div id="password-msg" class="msg-required"></div> + <input type="password" id="password" name="password" class="required" placeholder="$_("Password")" /> </div> <div class="row"> - <button id="btn-login" class="btn-normal">$_("Log in")</button> + <button id="btn-login" class="btn-normal" disabled="true">$_("Log in")</button> </div> </form> </div> diff --git a/ui/pages/storagepool-add.html.tmpl b/ui/pages/storagepool-add.html.tmpl index 977db66..6ce1343 100644 --- a/ui/pages/storagepool-add.html.tmpl +++ b/ui/pages/storagepool-add.html.tmpl @@ -36,7 +36,7 @@ <p class="text-help"> $_("The name used to identify the storage pools, and it should not be empty.") </p> - <input id="poolId" required="required" type="text" class="text storage-base-input-width" name="name"> + <input id="poolId" type="text" class="text storage-base-input-width required" name="name"> </div> </section> <section class="form-section"> @@ -60,7 +60,7 @@ $_("The path of the Storage Pool. Each Storage Pool must have a unique path.")</p> <p class="text-help"> $_("Kimchi will try to create the directory when it does not already exist in your system.")</p> - <input id="pathId" type="text" class="text storage-base-input-width"> + <input id="pathId" type="text" class="text storage-base-input-width required"> </div> <div class="clear"></div> </section> @@ -152,7 +152,7 @@ </div> <footer> <div class="btn-group"> - <button id="pool-doAdd" class="btn-normal"> + <button id="pool-doAdd" class="btn-normal" disabled="disabled"> <span class="text">$_("Create")</span> </button> </div> diff --git a/ui/pages/template-edit.html.tmpl b/ui/pages/template-edit.html.tmpl index 434d938..46b7058 100644 --- a/ui/pages/template-edit.html.tmpl +++ b/ui/pages/template-edit.html.tmpl @@ -36,7 +36,7 @@ <label for="template-edit-id-textbox">$_("Name")</label> </div> <div class="template-edit-wrapper-controls"> - <input id="template-edit-id-textbox" name="name" type="text" /> + <input id="template-edit-id-textbox" name="name" type="text" class="required" /> </div> </div> <div> @@ -60,7 +60,7 @@ <label for="template-edit-cpu-textbox">$_("CPU Number")</label> </div> <div class="template-edit-wrapper-controls"> - <input id="template-edit-cpu-textbox" name="cpus" type="text" /> + <input id="template-edit-cpu-textbox" name="cpus" type="text" class="required" /> </div> </div> <div> @@ -68,7 +68,7 @@ <label for="template-edit-memory-textbox">$_("Memory")</label> </div> <div class="template-edit-wrapper-controls"> - <input id="template-edit-memory-textbox" name="memory" type="text" /> + <input id="template-edit-memory-textbox" name="memory" type="text" class="required" /> </div> </div> <div> @@ -76,7 +76,7 @@ <label for="template-edit-disk-textbox">$_("Disk (GB)")</label> </div> <div class="template-edit-wrapper-controls"> - <input id="template-edit-disk-textbox" name="disks" type="text" /> + <input id="template-edit-disk-textbox" name="disks" type="text" class="required" /> </div> </div> </fieldset>
-- Thanks and best regards! Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

On 15-05-2014 02:59, Sheldon wrote:
need improve
After I apply this patch.
my browser remember the user name and password. But I can no login. I can login after I input any character and then delete it. You're right. I never allow my browser to save my password, that's why I didn't catch this scenario. I'll send another version which will check both fields as soon as the window is loaded.

On 05/14/2014 04:44 AM, Crístian Viana wrote:
Most forms contain fields that must be filled before they are submitted. Some of them submit the missing data and display an error message when something goes wrong, and some of them do not allow the user to submit them if some data is missing.
Make the forms behavior consistent by enabling their buttons only when the required data is entered. Internally, the page tags all required fields with the class "required" and whenever any of their values change, they are checke. If any required field is empty, the submit button becomses disabled; otherwise, it becomes enabled.
Fix GitHub issue #317 ("Inconsistent button status when adding or creating new resources").
Signed-off-by: Crístian Viana <vianac@linux.vnet.ibm.com> --- ui/css/theme-default/login-window.css | 4 -- ui/js/src/kimchi.guest_edit_main.js | 15 +++++++ ui/js/src/kimchi.login_window.js | 36 +++++++-------- ui/js/src/kimchi.storagepool_add_main.js | 75 +++++++++++++++++++++++--------- ui/js/src/kimchi.template_edit_main.js | 20 +++++++++ ui/pages/guest-edit.html.tmpl | 6 +-- ui/pages/i18n.html.tmpl | 6 --- ui/pages/login-window.html.tmpl | 8 ++-- ui/pages/storagepool-add.html.tmpl | 6 +-- ui/pages/template-edit.html.tmpl | 8 ++-- 10 files changed, 117 insertions(+), 67 deletions(-)
diff --git a/ui/css/theme-default/login-window.css b/ui/css/theme-default/login-window.css index 8a21090..d021be6 100644 --- a/ui/css/theme-default/login-window.css +++ b/ui/css/theme-default/login-window.css @@ -62,10 +62,6 @@ width: 290px; }
-#login-window .login-panel .msg-required { - color: red; -} - #login-window .login-panel button { font-size: 18px; height: 40px; diff --git a/ui/js/src/kimchi.guest_edit_main.js b/ui/js/src/kimchi.guest_edit_main.js index 0236e2d..bb4ee51 100644 --- a/ui/js/src/kimchi.guest_edit_main.js +++ b/ui/js/src/kimchi.guest_edit_main.js @@ -202,6 +202,21 @@ kimchi.guest_edit_main = function() {
kimchi.retrieveVM(kimchi.selectedGuest, initContent);
+ var updateSubmitButtonStatus = function() { + var valid = true; + guestEditForm.find('input.required').each(function(index, field) { + if (field.value === '') { + valid = false; + return; + } + }); + + saveButton.prop('disabled', !valid); + }; + + guestEditForm.find('input.required').on('input propertychange', + updateSubmitButtonStatus); + var submitForm = function(event) { $(saveButton).prop('disabled', true); var data=$('#form-guest-edit-general').serializeObject(); diff --git a/ui/js/src/kimchi.login_window.js b/ui/js/src/kimchi.login_window.js index 9c06a50..52394a5 100644 --- a/ui/js/src/kimchi.login_window.js +++ b/ui/js/src/kimchi.login_window.js @@ -30,26 +30,25 @@ kimchi.login_main = function() { $(opt).prop('selected', selectedLanguage === k); }
+ var updateSubmitButtonStatus = function() { + var valid = true; + $('#form-login').find('input.required').each(function(index, field) { + if (field.value === '') { + valid = false; + return; + } + }); + + $('#btn-login').prop('disabled', !valid); + } + + $('#form-login').find('input.required').on('input propertychange', + updateSubmitButtonStatus); + There's some problem with this new function. And there is no need to replace the current validateNonEmpty() function. In the current validateNonEmpty() function, several checks are processed:
1) If user name field is empty, then put cursor in user name box Else if password field is empty, then put cursor in password box Else put cursor on log in button 2) If some field is empty, a message will show to tell user the field is required.
$('#language').on('change', function() { kimchi.topic('languageChanged').publish($(this).val()); });
- var validateNonEmpty = function(idsArray) { - for(var i = 0; i < idsArray.length; i++) { - var id = idsArray[i]; - if (!$('#' + id).val()) { - $('#' + id + '-msg').text(i18n['KCHAUTH6002E']); - placeCursor(id); - return false; - } - else { - $('#' + id + '-msg').empty(); - } - } - - return true; - }; - var placeCursor = function(id) { if (id && $('#' + id).size() > 0) { $('#' + id).focus(); @@ -66,11 +65,6 @@ kimchi.login_main = function() { };
var login = function(event) { - - if (!validateNonEmpty(['username', 'password'])) { - return false; - } - $('#btn-login').text(i18n['KCHAUTH6002M']).prop('disabled', true);
var userName = $('#username').val(); diff --git a/ui/js/src/kimchi.storagepool_add_main.js b/ui/js/src/kimchi.storagepool_add_main.js index 86dbe7f..5356985 100644 --- a/ui/js/src/kimchi.storagepool_add_main.js +++ b/ui/js/src/kimchi.storagepool_add_main.js @@ -122,6 +122,18 @@ kimchi.initStorageAddPage = function() { }); });
+ var updateSubmitButtonStatus = function() { + var valid = true; + $('#form-pool-add').find('input.required').each(function(index, field) { + if (field.value === '') { + valid = false; + return; + } + }); + + $('#pool-doAdd').prop('disabled', !valid); + }; +
Adam is working on the global form validation item, so you can leave this function alone and work on other feature currently.
$('#poolTypeInputId').change(function() { var poolObject = {'dir': ".path-section", 'netfs': '.nfs-section', 'iscsi': '.iscsi-section', 'scsi': '.scsi-section', @@ -134,7 +146,49 @@ kimchi.initStorageAddPage = function() { $(value).addClass('tmpl-html'); } }); + + switch (selectType) { + case 'dir': + $('#pathId').addClass('required'); + + $('#nfsserverId').removeClass('required'); + $('#nfspathId').removeClass('required'); + $('#iscsiserverId').removeClass('required'); + break; + case 'netfs': + $('#nfsserverId').addClass('required'); + $('#nfspathId').addClass('required'); + + $('#pathId').removeClass('required'); + $('#iscsiserverId').removeClass('required'); + break; + case 'iscsi': + $('#iscsiserverId').addClass('required'); + + $('#pathId').removeClass('required'); + $('#nfsserverId').removeClass('required'); + $('#nfspathId').removeClass('required'); + break; + case 'scsi': + $('#pathId').removeClass('required'); + $('#nfsserverId').removeClass('required'); + $('#nfspathId').removeClass('required'); + $('#iscsiserverId').removeClass('required'); + break; + case 'logical': + $('#pathId').removeClass('required'); + $('#nfsserverId').removeClass('required'); + $('#nfspathId').removeClass('required'); + $('#iscsiserverId').removeClass('required'); + break; + } + + updateSubmitButtonStatus(); }); + + $('#form-pool-add').find('input').on('input propertychange', + updateSubmitButtonStatus); + $('#authId').click(function() { if ($(this).prop("checked")) { $('.authenticationfield').removeClass('tmpl-html'); @@ -150,10 +204,6 @@ kimchi.initStorageAddPage = function() { kimchi.validateForm = function() { var name = $('#poolId').val(); var poolType = $("#poolTypeInputId").val(); - if ('' === name) { - kimchi.message.error.code('KCHPOOL6001E'); - return false; - } if (name.indexOf("/")!=-1) { kimchi.message.error.code('KCHPOOL6004E'); return false; @@ -173,10 +223,6 @@ kimchi.validateForm = function() {
kimchi.validateDirForm = function () { var path = $('#pathId').val(); - if ('' === path) { - kimchi.message.error.code('KCHPOOL6002E'); - return false; - } if (!/(^\/.*)$/.test(path)) { kimchi.message.error.code('KCHAPI6003E'); return false; @@ -190,10 +236,6 @@ kimchi.validateNfsForm = function () { if (!kimchi.validateServer(nfsserver)) { return false; } - if ('' === nfspath) { - kimchi.message.error.code('KCHPOOL6003E'); - return false; - } if (!/((\/([0-9a-zA-Z-_\.]+)))$/.test(nfspath)) { kimchi.message.error.code('KCHPOOL6005E'); return false; @@ -203,22 +245,13 @@ kimchi.validateNfsForm = function () {
kimchi.validateIscsiForm = function() { var iscsiServer = $('#iscsiserverId').val(); - var iscsiTarget = $('#iscsiTargetId').val(); if (!kimchi.validateServer(iscsiServer)) { return false; } - if ('' === iscsiTarget) { - kimchi.message.error.code('KCHPOOL6007E'); - return false; - } return true; };
kimchi.validateServer = function(serverField) { - if ('' === serverField) { - kimchi.message.error.code('KCHPOOL6008E'); - return false; - } if(!kimchi.isServer(serverField)) { kimchi.message.error.code('KCHPOOL6009E'); return false; diff --git a/ui/js/src/kimchi.template_edit_main.js b/ui/js/src/kimchi.template_edit_main.js index f0f4718..e552bf9 100644 --- a/ui/js/src/kimchi.template_edit_main.js +++ b/ui/js/src/kimchi.template_edit_main.js @@ -104,6 +104,26 @@ kimchi.template_edit_main = function() { }); });
+ var updateSubmitButtonStatus = function() { + var valid = true; + templateEditForm.find('input.required').each(function(index, field) { + if (field.value === '') { + valid = false; + return; + } + }); + + if (valid) { + $('#tmpl-edit-button-save').removeAttr('disabled'); + } + else { + $('#tmpl-edit-button-save').attr('disabled', 'disabled'); + } + }; + + templateEditForm.find('input.required').on('input propertychange', + updateSubmitButtonStatus); + $('#tmpl-edit-button-cancel').on('click', function() { kimchi.window.close(); }); diff --git a/ui/pages/guest-edit.html.tmpl b/ui/pages/guest-edit.html.tmpl index 96d907e..d35ad7a 100644 --- a/ui/pages/guest-edit.html.tmpl +++ b/ui/pages/guest-edit.html.tmpl @@ -49,7 +49,7 @@ </div> <div class="guest-edit-wrapper-controls"> <input id="guest-edit-id-textbox" - name="name" type="text" /> + name="name" type="text" class="required" /> </div> </div> <div> @@ -62,7 +62,7 @@ <input id="guest-edit-cores-textbox" name="cpus" - type="text" /> + type="text" class="required" /> </div> </div> <div> @@ -74,7 +74,7 @@ <div class="guest-edit-wrapper-controls"> <input id="guest-edit-memory-textbox" name="memory" - type="text" /> + type="text" class="required" /> </div> </div> <div> diff --git a/ui/pages/i18n.html.tmpl b/ui/pages/i18n.html.tmpl index 98da828..814825f 100644 --- a/ui/pages/i18n.html.tmpl +++ b/ui/pages/i18n.html.tmpl @@ -33,7 +33,6 @@ <script> var i18n = { 'KCHAUTH6001E': "$_("The username or password you entered is incorrect. Please try again.")", - 'KCHAUTH6002E': "$_("This field is required.")",
'KCHAUTH6001M': "$_("Log in")", 'KCHAUTH6002M': "$_("Logging in...")", @@ -158,14 +157,9 @@ var i18n = { 'KCHPOOL6004M': "$_("SCSI Fibre Channel")", 'KCHPOOL6005M': "$_("No SCSI adapters found.")",
- 'KCHPOOL6001E': "$_("The storage pool name can not be blank.")", - 'KCHPOOL6002E': "$_("The storage pool path can not be blank.")", - 'KCHPOOL6003E': "$_("NFS server mount path can not be blank.")", 'KCHPOOL6004E': "$_("Invalid storage pool name. It should not contain '/'.")", 'KCHPOOL6005E': "$_("Invalid NFS mount path.")", 'KCHPOOL6006E': "$_("No logical device selected.")", - 'KCHPOOL6007E': "$_("The iSCSI target can not be blank.")", - 'KCHPOOL6008E': "$_("Server name can not be blank.")", 'KCHPOOL6009E': "$_("This is not a valid Server Name or IP. please, modify it.")", 'KCHPOOL6010M': "$_("Looking for available partitions ...")", 'KCHPOOL6011M': "$_("No available partitions found.")", diff --git a/ui/pages/login-window.html.tmpl b/ui/pages/login-window.html.tmpl index 3e451c4..1cd9e83 100644 --- a/ui/pages/login-window.html.tmpl +++ b/ui/pages/login-window.html.tmpl @@ -34,15 +34,13 @@ <div class="content login-panel"> <form id="form-login" action="/login" method="POST"> <div class="row">
- <input type="text" id="username" name="username" required="required" placeholder="$_("User Name")" /> - <div id="username-msg" class="msg-required"></div> + <input type="text" id="username" name="username" class="required" placeholder="$_("User Name")" /> </div> <div class="row"> - <input type="password" id="password" name="password" required="required" placeholder="$_("Password")" /> - <div id="password-msg" class="msg-required"></div> + <input type="password" id="password" name="password" class="required" placeholder="$_("Password")" /> </div> <div class="row"> - <button id="btn-login" class="btn-normal">$_("Log in")</button> + <button id="btn-login" class="btn-normal" disabled="true">$_("Log in")</button> Suggest not change this as mentioned above. </div> </form> </div> diff --git a/ui/pages/storagepool-add.html.tmpl b/ui/pages/storagepool-add.html.tmpl index 977db66..6ce1343 100644 --- a/ui/pages/storagepool-add.html.tmpl +++ b/ui/pages/storagepool-add.html.tmpl @@ -36,7 +36,7 @@ <p class="text-help"> $_("The name used to identify the storage pools, and it should not be empty.") </p> - <input id="poolId" required="required" type="text" class="text storage-base-input-width" name="name"> + <input id="poolId" type="text" class="text storage-base-input-width required" name="name"> </div> </section> <section class="form-section"> @@ -60,7 +60,7 @@ $_("The path of the Storage Pool. Each Storage Pool must have a unique path.")</p> <p class="text-help"> $_("Kimchi will try to create the directory when it does not already exist in your system.")</p> - <input id="pathId" type="text" class="text storage-base-input-width"> + <input id="pathId" type="text" class="text storage-base-input-width required"> </div> <div class="clear"></div> </section> @@ -152,7 +152,7 @@ </div> <footer> <div class="btn-group"> - <button id="pool-doAdd" class="btn-normal"> + <button id="pool-doAdd" class="btn-normal" disabled="disabled"> <span class="text">$_("Create")</span> </button> </div> diff --git a/ui/pages/template-edit.html.tmpl b/ui/pages/template-edit.html.tmpl index 434d938..46b7058 100644 --- a/ui/pages/template-edit.html.tmpl +++ b/ui/pages/template-edit.html.tmpl @@ -36,7 +36,7 @@ <label for="template-edit-id-textbox">$_("Name")</label> </div> <div class="template-edit-wrapper-controls"> - <input id="template-edit-id-textbox" name="name" type="text" /> + <input id="template-edit-id-textbox" name="name" type="text" class="required" /> </div> </div> <div> @@ -60,7 +60,7 @@ <label for="template-edit-cpu-textbox">$_("CPU Number")</label> </div> <div class="template-edit-wrapper-controls"> - <input id="template-edit-cpu-textbox" name="cpus" type="text" /> + <input id="template-edit-cpu-textbox" name="cpus" type="text" class="required" /> </div> </div> <div> @@ -68,7 +68,7 @@ <label for="template-edit-memory-textbox">$_("Memory")</label> </div> <div class="template-edit-wrapper-controls"> - <input id="template-edit-memory-textbox" name="memory" type="text" /> + <input id="template-edit-memory-textbox" name="memory" type="text" class="required" /> </div> </div> <div> @@ -76,7 +76,7 @@ <label for="template-edit-disk-textbox">$_("Disk (GB)")</label> </div> <div class="template-edit-wrapper-controls"> - <input id="template-edit-disk-textbox" name="disks" type="text" /> + <input id="template-edit-disk-textbox" name="disks" type="text" class="required" /> </div> </div> </fieldset>

On 15-05-2014 03:51, Hongliang Wang wrote:
There's some problem with this new function. And there is no need to replace the current validateNonEmpty() function. In the current validateNonEmpty() function, several checks are processed: Those checks don't make sense if the submit button is disabled while some of the required fields are still empty:
1) If user name field is empty, then put cursor in user name box If the user name field is empty, the user won't be able to click the log in button; no need to take them back to the user name field. Else if password field is empty, then put cursor in password box If the password field is empty, the user still won't be able to click the log in button; no need to take them back to the password field. Else put cursor on log in button
2) If some field is empty, a message will show to tell user the field is required. If some field is empty, the user won't be able to click the log in button; no need to display a message on the screen.
Again, I'm trying to make the Kimchi forms consistent. Some forms leave the submit button enabled all the time and when the user clicks it, the entire form is validated; other forms leave the submit button disabled and enable it only when the required fields are filled; some forms highlight the required fields when they're empty; some forms remove automatically the text entered by the user if it thinks the data is invalid (!!); some forms don't even have a submit button visible all the time... I'd like to see a single behavior regarding form validation across Kimchi. We can use a combination of those features I mentioned above (most of them are nice), but we must decide and use the same approach everywhere. Which is not the case right now. I chose one of them to create this patch (i.e. keeping the submit button disabled until all required fields are filled) so we can start discussing about consistency, but I agree we should decide something else better. As long as it's applied everywhere and the user always knows what to expect when browsing the application.

On 05/16/2014 05:08 AM, Crístian Viana wrote:
On 15-05-2014 03:51, Hongliang Wang wrote:
There's some problem with this new function. And there is no need to replace the current validateNonEmpty() function. In the current validateNonEmpty() function, several checks are processed: Those checks don't make sense if the submit button is disabled while some of the required fields are still empty:
1) If user name field is empty, then put cursor in user name box If the user name field is empty, the user won't be able to click the log in button; no need to take them back to the user name field.
Else if password field is empty, then put cursor in password box If the password field is empty, the user still won't be able to click the log in button; no need to take them back to the password field. Else put cursor on log in button
2) If some field is empty, a message will show to tell user the field is required. If some field is empty, the user won't be able to click the log in button; no need to display a message on the screen.
Again, I'm trying to make the Kimchi forms consistent. Some forms leave the submit button enabled all the time and when the user clicks it, the entire form is validated; other forms leave the submit button disabled and enable it only when the required fields are filled; some forms highlight the required fields when they're empty; some forms remove automatically the text entered by the user if it thinks the data is invalid (!!); some forms don't even have a submit button visible all the time... I'd like to see a single behavior regarding form validation across Kimchi. We can use a combination of those features I mentioned above (most of them are nice), but we must decide and use the same approach everywhere. Which is not the case right now. I chose one of them to create this patch (i.e. keeping the submit button disabled until all required fields are filled) so we can start discussing about consistency, but I agree we should decide something else better. As long as it's applied everywhere and the user always knows what to expect when browsing the application. OK. I understand your point and it's good, please go ahead to make it consistent. Though another aspect is that you need remain the consideration for user experience. Maybe there is misunderstanding for my "put cursor" lines. Let me explain it in more details.
Case#1 New user visits Kimchi for the first time There's no cookie set so the user name text box will not be filled by cookie value. So we are to place focus cursor into user name box to provide the convenience that allows user quickly press keys without use mouse to click the cursor into the box first and then start pressing keyboard. Case#2 User logged out Kimchi and visits Kimchi again later If user allows browser to save cookie, his user name was save into cookie and Kimchi code will load it automatically and fill it into user name box. So it's probably the user will use this same user name and he only wants to input his password. So we place focus cursor into password box. Case#3 User saved his user name and password In this case, the user only wants to press Enter key or click log-in button to log in. So we focus the log-in button by default to allow user log in by simply entering Enter key. Hopefully I explained it clearly.

On 16-05-2014 00:05, Hongliang Wang wrote:
OK. I understand your point and it's good, please go ahead to make it consistent. Though another aspect is that you need remain the consideration for user experience. Maybe there is misunderstanding for my "put cursor" lines. Let me explain it in more details.
Case#1 New user visits Kimchi for the first time There's no cookie set so the user name text box will not be filled by cookie value. So we are to place focus cursor into user name box to provide the convenience that allows user quickly press keys without use mouse to click the cursor into the box first and then start pressing keyboard.
Case#2 User logged out Kimchi and visits Kimchi again later If user allows browser to save cookie, his user name was save into cookie and Kimchi code will load it automatically and fill it into user name box. So it's probably the user will use this same user name and he only wants to input his password. So we place focus cursor into password box.
Case#3 User saved his user name and password In this case, the user only wants to press Enter key or click log-in button to log in. So we focus the log-in button by default to allow user log in by simply entering Enter key.
Hopefully I explained it clearly. Thanks for the clear explanation!
But I think we're talking about different things. I am trying to update the form validation behaviour here (i.e. when the submit will become enabled so it can be clicked, based on the user's input). You are talking about form initialization (i.e. how the form will be in its initial state, taking into account which fields are filled so the correct element is focused). I don't mean to change that now (even though it's a nice topic to discuss for future patches...), your explanation makes sense. What's important to me now is what will happen if the users types something / deletes something after the form is loaded.

On 05/23/2014 02:00 AM, Crístian Viana wrote: > On 16-05-2014 00:05, Hongliang Wang wrote: >> OK. I understand your point and it's good, please go ahead to make it >> consistent. >> Though another aspect is that you need remain the consideration for >> user experience. Maybe there is misunderstanding for my "put cursor" >> lines. Let me explain it in more details. >> >> Case#1 New user visits Kimchi for the first time >> There's no cookie set so the user name text box will not be filled >> by cookie value. So we are to place focus cursor into user name box >> to provide the convenience that allows user quickly press keys >> without use mouse to click the cursor into the box first and then >> start pressing keyboard. >> >> Case#2 User logged out Kimchi and visits Kimchi again later >> If user allows browser to save cookie, his user name was save into >> cookie and Kimchi code will load it automatically and fill it into >> user name box. So it's probably the user will use this same user name >> and he only wants to input his password. So we place focus cursor >> into password box. >> >> Case#3 User saved his user name and password >> In this case, the user only wants to press Enter key or click >> log-in button to log in. So we focus the log-in button by default to >> allow user log in by simply entering Enter key. >> >> Hopefully I explained it clearly. > Thanks for the clear explanation! > > But I think we're talking about different things. I am trying to > update the form validation behaviour here (i.e. when the submit will > become enabled so it can be clicked, based on the user's input). You > are talking about form initialization (i.e. how the form will be in > its initial state, taking into account which fields are filled so the > correct element is focused). I don't mean to change that now (even > though it's a nice topic to discuss for future patches...), your > explanation makes sense. What's important to me now is what will > happen if the users types something / deletes something after the form > is loaded. Yes I knew that. In fact, the UX of cursor functionality is partially broken. Let's take a specific example. 1. User logged in with kimchi/kimchi credential and let browser save the password. 2. Next time he logs in, browser will automatically fill the fields with saved credential. The cursor should be placed at log-in button and user can press enter to log in. Though with this patch applied, log-in button is disabled and it's confused for the user: everything is OK why I'm not allowed to log in? In current implementation, validateNonEmpty() has a call to placeCursor() so cursor will be taken into account every time we do validation. So in this patch, similar actions should be taken according to cursor.
participants (3)
-
Crístian Viana
-
Hongliang Wang
-
Sheldon