From 76bb21a15094f03c1a6d3376d86c28c72f1e6a6d Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Tue, 9 Aug 2016 23:17:35 -0500 Subject: [PATCH] summary page rebuild --- .../javascripts/jquery-linedtextarea-moser.js | 11 - app/assets/javascripts/live-coder.js.erb | 4 +- app/assets/javascripts/summary-edit.js | 228 +++++------------- app/assets/stylesheets/molecules/_forms.scss | 4 + .../stylesheets/molecules/_livecoder.scss | 14 +- app/controllers/quiz_controller.rb | 24 +- app/views/quiz/_answer_errors.html.erb | 2 +- app/views/quiz/_checkbox.html.erb | 28 +-- app/views/quiz/_live_code.html.erb | 7 +- app/views/quiz/_live_code_text.html.erb | 9 +- app/views/quiz/_radio.html.erb | 19 +- app/views/quiz/_text.html.erb | 5 +- app/views/quiz/summary.html.erb | 9 +- app/views/review/view.html.erb | 3 +- test/controllers/quiz_controller_test.rb | 2 +- test/fixtures/answers.yml | 24 +- test/workers/quiz_status_test.rb | 6 +- 17 files changed, 141 insertions(+), 258 deletions(-) diff --git a/app/assets/javascripts/jquery-linedtextarea-moser.js b/app/assets/javascripts/jquery-linedtextarea-moser.js index bea9ef9..e1059ae 100644 --- a/app/assets/javascripts/jquery-linedtextarea-moser.js +++ b/app/assets/javascripts/jquery-linedtextarea-moser.js @@ -87,17 +87,6 @@ } - /* Set the width */ - // var sidebarWidth = linesDiv.outerWidth(); - // var paddingHorizontal = parseInt( linedWrapDiv.css("border-left-width") ) + parseInt( linedWrapDiv.css("border-right-width") ) + parseInt( linedWrapDiv.css("padding-left") ) + parseInt( linedWrapDiv.css("padding-right") ); - // var linedWrapDivNewWidth = originalTextAreaWidth - paddingHorizontal; - // var textareaNewWidth = originalTextAreaWidth - sidebarWidth - paddingHorizontal - 20; - // - // textarea.width( textareaNewWidth ); - // linedWrapDiv.width( linedWrapDivNewWidth ); - - - /* React to the scroll event */ textarea.scroll( function(){ var domTextArea = $(this)[0]; diff --git a/app/assets/javascripts/live-coder.js.erb b/app/assets/javascripts/live-coder.js.erb index f559a09..d88b9e2 100644 --- a/app/assets/javascripts/live-coder.js.erb +++ b/app/assets/javascripts/live-coder.js.erb @@ -1,4 +1,6 @@ function updateResults(elem) { + if ($(elem).length ===0){return false;}; + var resultsContainer = $(elem).find('[data-id="results"]')[0]; var codeHtml = $(elem).find('.code-html')[0].value.trim(); var codeCss = $(elem).find('.code-css')[0].value.trim(); @@ -100,7 +102,7 @@ $(function(){ // wait a half second before updating results // restart the timer if they resume typing $('html').on('keyup', '.code-input textarea', function(){ - var elem = $(this).closest('.answer-sec.live_code-type, .answer-sec.live_code_text-type'); + var elem = $(this).closest("[data-id=live-coder-answer]"); if (timer) { clearTimeout(timer); } timer = setTimeout(updateResults(elem), 500); }); diff --git a/app/assets/javascripts/summary-edit.js b/app/assets/javascripts/summary-edit.js index e3ebf7f..5b2dc34 100644 --- a/app/assets/javascripts/summary-edit.js +++ b/app/assets/javascripts/summary-edit.js @@ -1,181 +1,83 @@ +/* global updateResults */ +/* TODO: remove global ^ once live-coder is properly name spaced */ /** * Summary Page Answer Editor */ +function disableForm($form){ + $form.find('fieldset').prop('disabled', true); + $form.find('textarea').prop('disabled', true); + $form.find('.button-save, .button-cancel').hide(); + $form.find('.button-edit').show(); + $form.find('.editable').removeClass('editable'); + $('.button-edit, .submit-button').removeClass('disabled-button'); +} -(function($){ - $.fn.setTextAreaHeight = function(input) { - return this.each(function(){ - var lineHeight = parseInt($(this).css('line-height')); - var rows = Math.ceil(input / lineHeight); - rows = rows === 0 ? 1 : rows; - - $(this).attr('rows', rows); - }); - }; - - $('input[type="radio"]').on('change', function() { - var inputName = $(this).attr('name'); - //$('input[name="'+inputName+'"]').attr('checked', false); - var value = $(this).attr('value'); - $('input[name="'+inputName+'"][value="'+value+'"]').attr("checked",true); - $('input[name="'+inputName+'"]').each(function() { - if($(this).val() != value) { - $(this).attr('checked', false); - } - }); +function restoreValues($form){ + $form.find('[type=radio][data-last], [type=checkbox][data-last]').each(function(){ + $(this).prop('checked', $(this).attr('data-last')); }); - // $('.run-js').hide().delay(); -}(jQuery)); + $form.find('textarea[data-last]').each(function(){ + $(this).val($(this).attr('data-last')); + }); +} -var existingValue = []; +function updateLocalValues($form){ + $form.find('[type=radio][data-last], [type=checkbox][data-last]').each(function(){ + $(this).attr('data-last', $(this).prop('checked') ? 'checked' : ''); + }); -var editClickHandler = function(e) { + $form.find('textarea[data-last]').each(function(){ + $(this).attr('data-last', $(this).val()); + }); +} + +function prepareAjax($form) { + $form.on("ajax:success", function(e, data){ + $form.prepend('
' + data.message + '
'); + disableForm($form); + updateLocalValues($form); + }).on("ajax:error", function(e, xhr) { + if (xhr.status === 400){ + $form.prepend('
' + xhr.responseJSON.join('
') + '
'); + } else { + $form.prepend('
Oops! There was an error processing your request. Please try again.
'); + } + }); +} + +function editClickHandler(e) { e.preventDefault(); - var thisEd = $(e.delegateTarget); - var height = thisEd.find('p').height(); - thisEd.data('answer', thisEd.find('p').text()); - if(thisEd.find('input').attr('type') == 'radio') { - existingValue = thisEd.find('input:checked').val(); - } - else if(thisEd.find('input').attr('type') == 'checkbox') { - $(thisEd.find('input')).each(function() { - if($(this).prop('checked') === true) { - existingValue.push($(this).val()); - } - }); - } - else if (thisEd.find('textarea:not(.code-answer)')) { - existingValue = thisEd.find('textarea:not(.code-answer)').val(); - thisEd.find('.chars.hidden').removeClass('hidden'); - } - $('.button-edit, .submit-button').addClass('disabled-button'); - thisEd.addClass('editable'); - // thisEd.find('.text-answer:not(.code-answer)').replaceWith(''); - thisEd.find('.answer-block, .code-answer').prop('disabled', false); - thisEd.find('textarea').setTextAreaHeight(height); - thisEd.find('textarea.answer-block').focus(); - thisEd.find('.button-edit').hide().delay(); - thisEd.find('.button-save, .button-cancel').show().delay(); - // thisEd.find('button.run-js').show().delay(); -}; + var $form = $(e.delegateTarget).closest('form'); + $(e.delegateTarget).addClass('editable'); + $form.find('fieldset').prop('disabled', false); + $form.find('textarea').prop('disabled', false); + $form.find('textarea').focus(); + $form.find('.button-edit').hide().delay(); + $form.find('.button-save, .button-cancel').show().delay(); +} -var cancelClickHandler = function(e) { +function cancelClickHandler(e) { e.preventDefault(); - var thisEd = $(e.delegateTarget); - if(thisEd.find('input').attr('type') == 'radio') { - $(thisEd.find('input')).each(function() { - if($(this).val()!=existingValue) { - $(this).attr('checked', false).prop('checked', false); - } - else { - $(this).prop('checked', true); - } - }); - } - else if(thisEd.find('input').attr('type') == 'checkbox') { - $(existingValue).each(function(index, value) { - thisEd.find('input[value="'+value+'"]').prop('checked', true); - }); - } - else if (thisEd.find('textarea:not(.code-answer)')) { - thisEd.find('textarea:not(.code-answer)').val(existingValue); - thisEd.find('.chars').addClass('hidden'); - } - $('.success, .error').remove(); - $('.button-edit, .submit-button').removeClass('disabled-button'); - thisEd.removeClass('editable'); - // thisEd.find('textarea:not(.code-answer)').replaceWith('

' + $.trim(thisEd.data('answer')) + '

'); - thisEd.find('.answer-block, .code-answer').prop('disabled', true); - thisEd.find('.button-edit').show(); - thisEd.find('.button-save, .button-cancel').hide(); - // thisEd.find('button.run-js').hide(); - existingValue = []; -}; + var $form = $(e.delegateTarget).closest('form'); + $form.find('.error, .success').remove(); + disableForm($form); + restoreValues($form); + updateResults($form.find("[data-id=live-coder-answer]")); +} -var saveClickHandler = function(e) { +function saveClickHandler(e) { e.preventDefault(); - var thisEd = $(e.delegateTarget); - var data =[]; - var executeQuery; - var questionId = thisEd.find('.button-edit').attr('data-questionId'); - var answerId = thisEd.find('.button-edit').attr('data-answerId'); + var $form = $(e.delegateTarget).closest('form'); + $form.find('.error, .success').remove(); + $form.submit(); +} - if (thisEd.hasClass('live_code-type')) { - var htmlAnswer = $(thisEd.find('textarea.code-html')[0]).val(); - var cssAnswer = $(thisEd.find('textarea.code-css')[0]).val(); - var jsAnswer = $(thisEd.find('textarea.code-js')[0]).val(); - data = { - 'live_code': { - 'html': htmlAnswer, - 'css': cssAnswer, - 'js': jsAnswer - } - }; - } else if(thisEd.hasClass('radio-type')) { - $(thisEd.find('input')).each(function() { - if($(this).prop('checked') === true) { - data = ({ - 'radio': $(this).val() - }); - } - }); - } else if(thisEd.hasClass('checkbox-type')) { - data = {'checkbox': []}; - - $(thisEd.find('input')).each(function() { - if($(this).prop('checked') === true) { - data.checkbox.push($(this).val()); - } - }); - } else { - data = {'text': thisEd.find('textarea').val()}; - } - - if(data === '') { - $(thisEd).before('
Please select or enter a value.
'); - } else { - thisEd.find('textarea:not(.code-answer)').replaceWith('

' + $.trim(thisEd.find('textarea').val()) + '

'); - var postUrl = thisEd.closest('form').attr('action'); - - $.ajax({ - type: "POST", - url: postUrl, - data: ({ - 'answer': $.extend(data, {'question_id': questionId, 'answer_id': answerId}), - 'submit': true - }), - success: function(){ //unused data - executeQuery = true; - }, - error: function(){ //unused data - executeQuery = false; - } - }).done(function() { - if(executeQuery === true) { - $('.success, .error').remove(); - $(thisEd).before('
Your answer has been updated successfully!
'); - $(thisEd).find('.code-answer').attr('disabled', true); - } - if(executeQuery === false) { - $('.error, .success').remove(); - $(thisEd).before('
Oops! There was an error processing your request. Please try again.
'); - } - }); - - $('.button-edit, .submit-button').removeClass('disabled-button'); - thisEd.removeClass('editable'); - thisEd.find('.answer-block').prop('disabled', true); - thisEd.find('.button-edit').show(); - thisEd.find('.button-save, .button-cancel').hide(); - } -}; - -$('.answer-block').prop('disabled', true); - -// Question events -$('.answer-sec') +$('.summary_tpl fieldset').prop('disabled', true); +$('.summary_tpl textarea').prop('disabled', true); +$('.summary_tpl form').each(function(){ prepareAjax($(this)); }); +$('.summary_tpl .answer-sec') .find('.button-cancel, .button-save').hide().end() .on('click', '.button-edit', editClickHandler) .on('click', '.button-cancel', cancelClickHandler) diff --git a/app/assets/stylesheets/molecules/_forms.scss b/app/assets/stylesheets/molecules/_forms.scss index 5a53c2d..6a0ed63 100644 --- a/app/assets/stylesheets/molecules/_forms.scss +++ b/app/assets/stylesheets/molecules/_forms.scss @@ -78,6 +78,10 @@ textarea { padding: 0 0 3rem; } +.summary_tpl textarea { + padding: 1rem 1rem 3rem; +} + [type="search"] { appearance: none; } diff --git a/app/assets/stylesheets/molecules/_livecoder.scss b/app/assets/stylesheets/molecules/_livecoder.scss index 56ef959..07ca65e 100644 --- a/app/assets/stylesheets/molecules/_livecoder.scss +++ b/app/assets/stylesheets/molecules/_livecoder.scss @@ -2,18 +2,12 @@ margin: 10px 0; textarea { - background-color: #fff; - border: 1px solid black; font-family: "Lucida Console", Monaco, monospace; font-size: 10px; // line-height: 1.6em; margin-bottom: 0; min-height: 205px; width: 100%; - - &[disabled] { - background-color: #ddd; - } } } @@ -44,12 +38,6 @@ iframe { width: 100%; } -.live-coder-summary{ - button.update-button{ - display: none; - } -} - @media only screen and (min-width: $desktop) { .code-input { float: left; @@ -107,7 +95,7 @@ iframe { .linedtextarea textarea { padding-right: 0.3em; padding-top: 0.3em; - border: 0; + border: 0 !important; } .linedwrap .lines { diff --git a/app/controllers/quiz_controller.rb b/app/controllers/quiz_controller.rb index e87593d..1b50e63 100644 --- a/app/controllers/quiz_controller.rb +++ b/app/controllers/quiz_controller.rb @@ -10,9 +10,10 @@ class QuizController < ApplicationController end def update_answer - qid = answer_params[:question_id] || prep_status.current_question_id - @answer = prep_answer qid - send "process_#{prep_question(qid).input_type}" + @answer = prep_answer answer_params[:question_id] + send "process_#{prep_question(answer_params[:question_id]).input_type}" + route_remote and return if request.xhr? + route_answer end def summary @@ -65,49 +66,50 @@ class QuizController < ApplicationController if @answer.errors.present? prep_status prep_question answer_params[:question_id] - flash[:answer_error] = answer_params[:question_id].to_i + flash[:error] = answer_params[:question_id].to_i render :question else - flash.delete(:answer_error) # TODO: change params.key? to submit = save/next/summary - # redirect_to :summary and return if params.key?(:update) redirect_to :saved and return if params.key?(:save) redirect_to :question end end + def route_remote + if @answer.errors.present? + render json: @answer.errors["answer"].to_json, status: 400 + else + render json: { message: "Your answer has been updated successfully!" }.to_json + end + end + def process_text @answer.update(answer: answer_params[:text], saved: params.key?(:save), submitted: params.key?(:submit)) - route_answer end def process_radio @answer.update(answer: answer_params[:radio], saved: params.key?(:save), submitted: params.key?(:submit)) - route_answer end def process_checkbox @answer.update(answer: answer_params[:checkbox], saved: params.key?(:save), submitted: params.key?(:submit)) - route_answer end def process_live_code @answer.update(answer: answer_params[:live_code].to_h, saved: params.key?(:save), submitted: params.key?(:submit)) - route_answer end def process_live_code_text @answer.update(answer: answer_params[:live_code_text].to_h, saved: params.key?(:save), submitted: params.key?(:submit)) - route_answer end end diff --git a/app/views/quiz/_answer_errors.html.erb b/app/views/quiz/_answer_errors.html.erb index fe8d226..2f338a7 100644 --- a/app/views/quiz/_answer_errors.html.erb +++ b/app/views/quiz/_answer_errors.html.erb @@ -1,4 +1,4 @@ -<% if flash[:answer_error] == question.question_id && answer.present? %> +<% if flash[:error] == question.question_id && answer.try(:errors) %> <% answer.errors.messages[:answer].each do |message| %>
<%= message %>
<% end %> diff --git a/app/views/quiz/_checkbox.html.erb b/app/views/quiz/_checkbox.html.erb index 4e8a2b5..f1062b3 100644 --- a/app/views/quiz/_checkbox.html.erb +++ b/app/views/quiz/_checkbox.html.erb @@ -1,18 +1,18 @@ -<% - question.input_options.each_with_index do | option, i | - option_id = "#{question.question_id}_#{i}" +<% question.input_options.each_with_index do | option, i | + option_id = "#{question.question_id}_#{i}" - checkbox_html = {class: 'checkbox', - id: "answer_#{option_id}", - name: "answer[checkbox][]", - checked: Array(question.answer).include?(option) - } - answers = answer.try(:answer) || answer -%> -
- <%= form.check_box(:answer, checkbox_html, option, '') %> - <%= form.label(option_id, option) %> -
+ checkbox_html = {class: 'checkbox', + id: "answer_#{option_id}", + name: "answer[checkbox][]", + checked: Array(question.answer).include?(option), + data: { last: Array(question.answer).include?(option) ? 'checked' : '' } + } + answers = answer.try(:answer) || answer + %> +
+ <%= form.check_box(:answer, checkbox_html, option, '') %> + <%= form.label(option_id, option) %> +
<% end %> <%= render partial: "quiz/answer_errors", locals: {question: question, answer: answer} %> diff --git a/app/views/quiz/_live_code.html.erb b/app/views/quiz/_live_code.html.erb index 20de13f..ea220bf 100644 --- a/app/views/quiz/_live_code.html.erb +++ b/app/views/quiz/_live_code.html.erb @@ -5,7 +5,6 @@ name: "answer[#{question.input_type}][later]", checked: Array(question.answer).include?('finish-later') } - disabled = local_assigns.fetch :disable_input, false answers = answer.try(:answer) || answer %> @@ -31,17 +30,17 @@