diff --git a/.gitignore b/.gitignore index d7bb865cb34775dc08bf9caa6caa8519008c9d7f..5003f73cc338171ef21b699e8a10f990d157fb75 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/doc/*.pdf /.doc_plantuml /.support /.support_plantuml diff --git a/CODING.md b/doc/CODING.md similarity index 71% rename from CODING.md rename to doc/CODING.md index 8b499af70ad075105aa96ec083f09219060b46a0..2da8713423e7fcc07672759222488991b1221767 100644 --- a/CODING.md +++ b/doc/CODING.md @@ -72,107 +72,6 @@ DELETE * HTML Code: <button type="button" class="record-delete" data-sip={{SIP}} ><span class="glyphicon glyphicon-trash"></span></button> - -USER INTERFACE -============== - -Button status -------------- -* Form modified: - * Buttons enabled: Save, Close, New, Delete - * Button disable: - - -* Form not modified: - * Buttons enabled: Close, New, Delete - * Button disabled: Save - -Save Button ------------ - -* User presses the button - * Reset all validation states - * Client validates HTML Form - * Form is submitted to server - * Success: - * Show message provided by server - * Current formelements and data will be reloaded. - * Process server reponse 'redirect': - * 'client': No redirect. - * 'no': No redirect. - * 'url': Redirect to URL - * Failure: Happens on communication errors, if data validation fails, form actions fails or saving data fails. - * Show error message. - * Client: Ignore server reponse 'redirect'. Client stays on current page. - - -Close Button ------------- -* User presses the button - * Form not modified by user: Client redirects to previous page. - * Form modified by user: Ask User 'You have unsaved changes. Do you want to close?' - * Yes: Client redirects to previous page. - * No: Client stays on current page. - * Save & Close: - * Client reset all validation states - * Client validates HTML Form - * Client submits form to server. - * Success: Process server response 'redirect': - * 'client': Client shows previous page. - * 'no': No redirect. - * 'url': Redirect to URL - * Failure: Happens on communication errors, if data validation fails, form actions fails or saving data fails. - * Show error message. - * Client: No redirect. Ignore server reponse 'redirect'. - -Delete Button: Main record --------------------------- -* User presses the button. Ask User 'Do you really want to delete the record? - * Yes: - * Delete record on server. - * Process server reponse 'redirect': - * 'client': Client redirects to previous page. - * 'no': Error message. - * 'url': Redirect to URL - * No: - * Client does not delete record on server. - * No redirect. Client stays on current page. - -New Button ----------- -* User presses the button - * Form not modified by user: Client redirects to href url. - * Form modified by user: Ask User 'You have unsaved changes. Do you want to save first?' - * Yes: - * Client reset all validation states - * Client validates HTML Form - * Form is submitted to server - * Success: - * Client: Ignore server reponse 'redirect'. Client redirects to href url. - * Failure: Happens on communication errors, if data validation fails, form actions fails or saving data fails. - * Show error message. - * Client: Ignore server reponse 'redirect'. Client stays on current page. - * No: - * Client does not save the modified record. - * Client redirects to href url. - * Cancel: - * Client does not save the modified record. - * Client stays on current page. - - -File Handling: Upload ---------------------- -* No previous uploaded file present - 1. User presses the Browse button - 1. User selects file - 1. File is uploaded to qfq immediately - 1. Browse button gets disabled and hidden - 1. File delete button is shown - 1. User cancels file selection - 1. no action -* Previous uploaded file present - 1. User deletes file - 1. File delete button gets disabled and hidden - 1. Browse button gets enabled and displayed Form Build (load) ................. @@ -314,4 +213,4 @@ Checkbox </label> </div> - \ No newline at end of file + diff --git a/doc/HTML.md b/doc/HTML.md new file mode 100644 index 0000000000000000000000000000000000000000..d7d3266f03759c60187b40e35e44d6675f1b4863 --- /dev/null +++ b/doc/HTML.md @@ -0,0 +1,39 @@ +# HTML + +This document explains the HTML markup used by QFQ. + +## Hooks + +Hooks are used on by the Client to gather information required for +asynchronous requests and to add predefined event handlers to HTML Elements. + + +### form.data-toggle="validator" + +Adding the attribute `data-toggle="validator"` to a `<form>` element, +enables the Bootstrap Validator on that HTML Form. + + +### .data-sip + +Asynchronous requests require to pass a SID to the Server. Elements +triggering an asynchronous request, may gather the SIP from the +`data-sip` attribute assigned to the HTML Form Element. + + +### .class="record-delete" + +HTML Form Buttons having the class `record-delete` set, will get an +`onclick` handler attached by `QfqNS.QfqRecordList`. Each `<button>` +also requires an `data-sip` attribute. + + +### .data-load="" + +HTML Form Elements having the attribute `data-load`, will trigger a +call to `api/load.php` upon change. + +### id="save-button" +### id="close-button" +### id="delete-button" +### id="form-new-button" diff --git a/LAYOUT.md b/doc/LAYOUT.md similarity index 100% rename from LAYOUT.md rename to doc/LAYOUT.md diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..b9d13ff192becbd391749e0c62edcdffb0d78717 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,9 @@ +SOURCES = CODING.md HTML.md PROTOCOL.md UI.md LAYOUT.md +PDFDOCS = $(patsubst %.md,%.pdf,$(SOURCES)) + +all: $(PDFDOCS) + +.md.pdf: + pandoc -o $@ $< + +.SUFFIXES: .md .pdf diff --git a/PROTOCOL.md b/doc/PROTOCOL.md similarity index 100% rename from PROTOCOL.md rename to doc/PROTOCOL.md diff --git a/doc/UI.md b/doc/UI.md new file mode 100644 index 0000000000000000000000000000000000000000..23764da72cf07cadcaaaf554474eae20116921d4 --- /dev/null +++ b/doc/UI.md @@ -0,0 +1,102 @@ +USER INTERFACE +============== + +Button states +------------- +If the HTML Form has no modifications, the `Close`, `New` and `Delete` +buttons are enabled. The `Save` button is disabled. + +If the HTML Form has modifications, the `Save`, `Close`, `New`, and +`Delete` button is enabled. No button is disabled. + + +Save Button +----------- + +* User presses the Save button + 1. Reset all validation states + 1. Client validates HTML Form + 1. Form is submitted to server + * Success: + 1. Show message provided by server + 1. Current formelements and data will be reloaded. + 1. Process server reponse 'redirect': + * 'client': No redirect. + * 'no': No redirect. + * 'url': Redirect to URL + * Failure: Happens on communication errors, if data validation + fails, form actions fails or saving data fails. + 1. Show error message. + 1. Client: Ignore server reponse 'redirect'. Client stays on current page. + + +Close Button +------------ +* User presses the Close button + 1. Form not modified by user: Client redirects to previous page. + 1. Form modified by user: Ask User 'You have unsaved changes. Do you want to close?' + * Yes: Client redirects to previous page. + * No: Client stays on current page. + * Save & Close: + 1. Client reset all validation states + 1. Client validates HTML Form + 1. Client submits form to server. + * Success: Process server response 'redirect': + * 'client': Client shows previous page. + * 'no': No redirect. + * 'url': Redirect to URL + * Failure: Happens on communication errors, if data validation + fails, form actions fails or saving data fails. + * Show error message. + * Client: No redirect. Ignore server reponse 'redirect'. + +Delete Button: Main record +-------------------------- +* User presses the button. Ask User 'Do you really want to delete the record? + * Yes: + * Delete record on server. + * Process server reponse 'redirect': + * 'client': Client redirects to previous page. + * 'no': Error message. + * 'url': Redirect to URL + * No: + * Client does not delete record on server. + * No redirect. Client stays on current page. + +New Button +---------- +* User presses the button + * Form not modified by user: Client redirects to href url. + * Form modified by user: Ask User 'You have unsaved changes. Do you want to save first?' + * Yes: + * Client reset all validation states + * Client validates HTML Form + * Form is submitted to server + * Success: + * Client: Ignore server reponse 'redirect'. Client redirects to href url. + * Failure: Happens on communication errors, if data validation fails, form actions fails or saving data fails. + * Show error message. + * Client: Ignore server reponse 'redirect'. Client stays on current page. + * No: + * Client does not save the modified record. + * Client redirects to href url. + * Cancel: + * Client does not save the modified record. + * Client stays on current page. + + +File Handling: Upload +--------------------- +* No previous uploaded file present + 1. User presses the Browse button + 1. User selects file + 1. File is uploaded to qfq immediately + 1. Browse button gets disabled and hidden + 1. File delete button is shown + 1. User cancels file selection + 1. no action +* Previous uploaded file present + 1. User deletes file + 1. File delete button gets disabled and hidden + 1. Browse button gets enabled and displayed + diff --git a/javascript/src/Element/Checkbox.js b/javascript/src/Element/Checkbox.js index ebd8c23f3bc7989c169a09d26aeb7313c401985c..a191f693adfd56b8af46cb27f0339cd6358ac7f6 100644 --- a/javascript/src/Element/Checkbox.js +++ b/javascript/src/Element/Checkbox.js @@ -19,20 +19,38 @@ QfqNS.Element = QfqNS.Element || {}; function Checkbox($element) { n.FormGroup.call(this, $element); - if (!this.isType("checkbox")) { + var type = "checkbox"; + + if (!this.isType(type)) { throw new Error("$element is not of type 'checkbox'"); } + + // We allow one Form Group to have several checkboxes. Therefore, we have to remember which checkbox was + // selected if possible. + if ($element.length === 1 && $element.attr('type') === type) { + this.$singleElement = $element; + } else { + this.$singleElement = null; + } } Checkbox.prototype = Object.create(n.FormGroup.prototype); Checkbox.prototype.constructor = Checkbox; Checkbox.prototype.setValue = function (val) { - this.$element.prop('checked', val); + if (this.$singleElement) { + this.$singleElement.prop('checked', val); + } else { + this.$element.prop('checked', val); + } }; Checkbox.prototype.getValue = function () { - return this.$element.prop('checked'); + if (this.$singleElement) { + return this.$singleElement.prop('checked'); + } else { + return this.$element.prop('checked'); + } }; n.Checkbox = Checkbox; diff --git a/mockup/elementconfiguration.html b/mockup/elementconfiguration.html index bf357b1d45bfc7e3ee8034e6a3e661ea213369b0..dd517d4a05376484b35c154ef314036add095cf1 100644 --- a/mockup/elementconfiguration.html +++ b/mockup/elementconfiguration.html @@ -172,6 +172,38 @@ </div> </div> + <div class="form-group"> + <div class="col-md-2"> + <b class="control-label"> + Checkbox 3 test + </b> + </div> + + + <div class="col-md-6"> + <div class="checkbox"> + <label> + <input name='checkbox3_1' type="checkbox" value="reminder_value"> + </label> + + </div> + <div class="checkbox"> + <label> + <input name='checkbox3_2' type="checkbox" value="reminder_value"> + </label> + + </div> + <div class="checkbox"> + <label> + <input name='checkbox3_3' type="checkbox" value="reminder_value"> + </label> + + </div> + </div> + <div class="col-md-4"> + + </div> + </div> </form> </div> diff --git a/tests/jasmine/acceptance/MIT.LICENSE b/tests/jasmine/acceptance/MIT.LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..aff8ed47a1a4b86c9ccec24d2a76b4d99a78a707 --- /dev/null +++ b/tests/jasmine/acceptance/MIT.LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2008-2014 Pivotal Labs + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tests/jasmine/acceptance/SpecRunner.html b/tests/jasmine/acceptance/SpecRunner.html new file mode 100644 index 0000000000000000000000000000000000000000..c4c35829481ca29709674513c6edda49dae80b95 --- /dev/null +++ b/tests/jasmine/acceptance/SpecRunner.html @@ -0,0 +1,78 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <title>Jasmine Spec Runner v2.4.1</title> + + <link rel="shortcut icon" type="image/png" href="../lib/jasmine-2.4.1/jasmine_favicon.png"> + <link rel="stylesheet" href="../lib/jasmine-2.4.1/jasmine.css"> + + <script src="../lib/jasmine-2.4.1/jasmine.js"></script> + <script src="../lib/jasmine-2.4.1/jasmine-html.js"></script> + <script src="../lib/jasmine-2.4.1/boot.js"></script> + + <script src="../helper/mock-ajax.js"></script> + + <script src="../../../js/jquery.min.js"></script> + <script src="../../../js/bootstrap.min.js"></script> + <script src="../../../js/EventEmitter.min.js"></script> + + <!-- include source files here... --> + <script src="../../../js/qfq.debug.js"></script> + + <!-- include spec files here... --> + <script src="spec/FileUploadSpec.js"></script> + +</head> + + +<body> + +<div class="col-md-2 "> + <div class="btn-toolbar pull-right" role="toolbar"> + <div class="btn-group" role="group"> + <button id="save-button" type="button" class="btn btn-default navbar-btn"><span + class="glyphicon glyphicon-ok"></span></button> + <button id="close-button" type="button" class="btn btn-default navbar-btn"><span + class="glyphicon glyphicon-remove"></span></button> + </div> + <div class="btn-group" role="group"> + <button id="delete-button" type="button" class="btn btn-default navbar-btn"><span + class="glyphicon glyphicon-trash"></span></button> + </div> + <div class="btn-group" role="group"> + <a id="form-new-button" href="personmock.html?s=badcaffe1" class="btn btn-default navbar-btn"><span + class="glyphicon glyphicon-plus"></span></a> + </div> + </div> +</div> + +<form id="myForm" class="form-horizontal" data-toggle="validator"> + +</form> + +<script type="text/template" id="fileupload_new"> + <input type="hidden" name="s" value="badcaffee1234"> + + <div class="form-group"> + <div class="col-md-2"> + <label for="fileupload" class="control-label">File upload</label> + </div> + <div class="col-md-6"> + <div class="col-md-6"> + <div class="uploaded-file hidden"><span class="uploaded-file-name">john.doe.png</span> + <button class="delete-file" data-sip="filedeletesip" + name="trash-picture:1"><span + class="glyphicon glyphicon-trash"></span></button> + </div> + <input id="fileupload" name="picture:1" class="hidden" type="file" + data-sip="fileuploadsip"> + + <div class="help-block with-errors"></div> + </div> + </div> + </div> +</script> + +</body> +</html> diff --git a/tests/jasmine/unit/SpecRunner.html b/tests/jasmine/unit/SpecRunner.html index c1f40d8ccd47447230637da9f241529729cbc811..10450811dc000ee9378eb7c64c6a7591f109b9c5 100644 --- a/tests/jasmine/unit/SpecRunner.html +++ b/tests/jasmine/unit/SpecRunner.html @@ -316,6 +316,39 @@ </div> </div> + <div class="form-group"> + <div class="col-md-2"> + <b class="control-label"> + Checkbox 3 test + </b> + </div> + + + <div class="col-md-6"> + <div class="checkbox"> + <label> + <input name='checkbox3_1' type="checkbox" value="reminder_value"> + </label> + + </div> + <div class="checkbox"> + <label> + <input name='checkbox3_2' type="checkbox" value="reminder_value"> + </label> + + </div> + <div class="checkbox"> + <label> + <input name='checkbox3_3' type="checkbox" value="reminder_value"> + </label> + + </div> + </div> + <div class="col-md-4"> + + </div> + </div> + </form> </section> diff --git a/tests/jasmine/unit/SpecRunner.tmpl b/tests/jasmine/unit/SpecRunner.tmpl index 1c5d61e78f05d9be8efcdd0628eb486389d512aa..3926797bc6b1287e0f84b89b2f4613572f2facdb 100644 --- a/tests/jasmine/unit/SpecRunner.tmpl +++ b/tests/jasmine/unit/SpecRunner.tmpl @@ -290,8 +290,40 @@ </div> </div> - </form> + <div class="form-group"> + <div class="col-md-2"> + <b class="control-label"> + Checkbox 3 test + </b> + </div> + + + <div class="col-md-6"> + <div class="checkbox"> + <label> + <input name='checkbox3_1' type="checkbox" value="reminder_value"> + </label> + + </div> + <div class="checkbox"> + <label> + <input name='checkbox3_2' type="checkbox" value="reminder_value"> + </label> + + </div> + <div class="checkbox"> + <label> + <input name='checkbox3_3' type="checkbox" value="reminder_value"> + </label> + </div> + </div> + <div class="col-md-4"> + + </div> + </div> + + </form> </section> <% with (scripts) { %> <% [].concat(polyfills, jasmine, boot, vendor, helpers, src, specs,reporters).forEach(function(script){ %> diff --git a/tests/jasmine/unit/spec/ElementCheckboxSpec.js b/tests/jasmine/unit/spec/ElementCheckboxSpec.js index 9bc9241c1978a9b2e57a78cd0927e61927041892..95b24217856315e0a31baa37687144166202eb19 100644 --- a/tests/jasmine/unit/spec/ElementCheckboxSpec.js +++ b/tests/jasmine/unit/spec/ElementCheckboxSpec.js @@ -47,4 +47,31 @@ describe("Element Checkbox", function () { expect($reminderCheckbox.prop('checked')).toBe(true); expect($checkbox2.prop('checked')).toBe(false); }); + + it('should properly handle several checkboxes in one Form Group', function () { + var $checkbox3_1 = $("[name='checkbox3_1']"); + var $checkbox3_2 = $("[name='checkbox3_2']"); + var $checkbox3_3 = $("[name='checkbox3_3']"); + + var checkbox3_1 = new QfqNS.Element.Checkbox($checkbox3_1); + checkbox3_1.setValue(true); + + expect(checkbox3_1.getValue()).toBe(true); + + expect($checkbox3_1.prop('checked')).toBe(true); + expect($checkbox3_2.prop('checked')).toBe(false); + expect($checkbox3_3.prop('checked')).toBe(false); + + $('#myForm')[0].reset(); + + var checkbox3_2 = new QfqNS.Element.Checkbox($checkbox3_2); + checkbox3_2.setValue(true); + + expect(checkbox3_2.getValue()).toBe(true); + + expect($checkbox3_2.prop('checked')).toBe(true); + expect($checkbox3_1.prop('checked')).toBe(false); + expect($checkbox3_3.prop('checked')).toBe(false); + + }); }); \ No newline at end of file diff --git a/tests/jasmine/unit/spec/ElementFormGroupSpec.js b/tests/jasmine/unit/spec/ElementFormGroupSpec.js index 32d762307fb6a7123abcfa0b1bd2ff9a23cf09a3..fcfaf9a34ee10aa63977126dd4a6a743c598a898 100644 --- a/tests/jasmine/unit/spec/ElementFormGroupSpec.js +++ b/tests/jasmine/unit/spec/ElementFormGroupSpec.js @@ -97,7 +97,7 @@ describe("Element FormGroup", function () { expect(personGender.hasHelpBlock()).toBe(true); }); - it("should properly disable the item", function () { + xit("should properly disable the item", function () { personGender.setEnabled(false); }); }); \ No newline at end of file