From 176e5a5066ec0248c80d15af60a6be237cb450d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Warnon?= Date: Fri, 20 Mar 2020 14:28:14 +0000 Subject: [PATCH] [IMP] survey: improve survey form navigation This commit slightly changes the way users navigate through the survey form: - The back button was moved to the bottom-right of the screen. - The "Continue" button stays at the center but there is also a "next" button at the bottom-right of the screen (same behavior -> submits the form) - Arrow keys (left + right) navigation is now supported Technical note: As the survey navigation is now outside of the survey content, that was re-rendered on every submit, we have to manually refresh the navigation with a second template render on the controller side. LINKS PR #46768 Task 2208574 --- addons/survey/controllers/main.py | 12 ++++--- addons/survey/models/survey_survey.py | 24 +++++++++++++ addons/survey/static/src/js/survey_form.js | 35 +++++++++++++++---- .../survey/static/src/scss/survey_form.scss | 4 +++ addons/survey/views/survey_templates.xml | 15 ++------ .../views/survey_templates_management.xml | 11 ++++++ 6 files changed, 78 insertions(+), 23 deletions(-) diff --git a/addons/survey/controllers/main.py b/addons/survey/controllers/main.py index e8b72629ab0..8f931ae5cf8 100644 --- a/addons/survey/controllers/main.py +++ b/addons/survey/controllers/main.py @@ -268,10 +268,12 @@ class Survey(http.Controller): if 'previous_page_id' in post: previous_page_or_question_id = int(post['previous_page_id']) new_previous_id = survey_sudo._get_next_page_or_question(answer_sudo, previous_page_or_question_id, go_back=True).id + page_or_question = request.env['survey.question'].sudo().browse(previous_page_or_question_id) data.update({ - page_or_question_key: request.env['survey.question'].sudo().browse(previous_page_or_question_id), + page_or_question_key: page_or_question, 'previous_page_id': new_previous_id, - 'has_answered': answer_sudo.user_input_line_ids.filtered(lambda line: line.question_id.id == new_previous_id) + 'has_answered': answer_sudo.user_input_line_ids.filtered(lambda line: line.question_id.id == new_previous_id), + 'can_go_back': survey_sudo._can_go_back(answer_sudo, page_or_question), }) return data @@ -296,7 +298,8 @@ class Survey(http.Controller): data.update({ page_or_question_key: next_page_or_question, - 'has_answered': answer_sudo.user_input_line_ids.filtered(lambda line: line.question_id == next_page_or_question) + 'has_answered': answer_sudo.user_input_line_ids.filtered(lambda line: line.question_id == next_page_or_question), + 'can_go_back': survey_sudo._can_go_back(answer_sudo, next_page_or_question), }) if survey_sudo.questions_layout != 'one_page': data.update({ @@ -338,7 +341,8 @@ class Survey(http.Controller): return { 'survey_content': survey_content, - 'survey_progress': survey_progress + 'survey_progress': survey_progress, + 'survey_navigation': request.env.ref('survey.survey_navigation').render(survey_data), } @http.route('/survey//', type='http', auth='public', website=True) diff --git a/addons/survey/models/survey_survey.py b/addons/survey/models/survey_survey.py index ee71963b982..c5d0c5b1367 100644 --- a/addons/survey/models/survey_survey.py +++ b/addons/survey/models/survey_survey.py @@ -445,6 +445,30 @@ class Survey(models.Model): return questions + def _can_go_back(self, answer, page_or_question): + """ Check if the user can go back to the previous question/page for the currently + viewed question/page. + Back button needs to be configured on survey and, depending on the layout: + - In 'page_per_section', we can go back if we're not on the first page + - In 'page_per_question', we can go back if: + - It is not a session answer (doesn't make sense to go back in session context) + - We are not on the first question + - The survey does not have pages OR this is not the first page of the survey + (pages are displayed in 'page_per_question' layout when they have a description, see PR#44271) + """ + self.ensure_one() + + if self.users_can_go_back and answer.state == 'in_progress': + if self.questions_layout == 'page_per_section' and page_or_question != self.page_ids[0]: + return True + elif self.questions_layout == 'page_per_question' and \ + not answer.is_session_answer and \ + page_or_question != answer.predefined_question_ids[0] \ + and (not self.page_ids or page_or_question != self.page_ids[0]): + return True + + return False + def _has_attempts_left(self, partner, email, invite_token): self.ensure_one() diff --git a/addons/survey/static/src/js/survey_form.js b/addons/survey/static/src/js/survey_form.js index 723ae3c30ab..9827062e6b4 100644 --- a/addons/survey/static/src/js/survey_form.js +++ b/addons/survey/static/src/js/survey_form.js @@ -48,7 +48,7 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({ self._focusOnFirstInput(); // Init event listener if (!self.readonly) { - $(document).on('keypress', self._onKeyPress.bind(self)); + $(document).on('keydown', self._onKeyDown.bind(self)); } if (self.options.sessionInProgress && (self.options.isStartScreen || self.options.hasAnswered || self.options.isPageDescription)) { @@ -56,9 +56,11 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({ } self._initSessionManagement(); - // Needs global selector as the progress is not within the survey form, but needs to be - // updated at the same time + // Needs global selector as progress/navigation are not within the survey form, but need + //to be updated at the same time self.$surveyProgress = $('.o_survey_progress_wrapper'); + self.$surveyNavigation = $('.o_survey_navigation_wrapper'); + self.$surveyNavigation.find('.o_survey_navigation_submit').on('click', self._onSubmit.bind(self)); }); }, @@ -69,9 +71,18 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({ // Handlers // ------------------------------------------------------------------------- - _onKeyPress: function (event) { - // If user is answering a textarea, do not handle keyPress - if (this.$("textarea").is(":focus")) { + /** + * Handle keyboard navigation: + * - 'enter' or 'arrow-right' => submit form + * - 'arrow-left' => submit form (but go back backwards) + * - other alphabetical character ('a', 'b', ...) + * Select the related option in the form (if available) + * + * @param {Event} event + */ + _onKeyDown: function (event) { + // If user is answering a text input, do not handle keydown + if (this.$("textarea").is(":focus") || this.$('input').is(':focus')) { return; } @@ -80,12 +91,17 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({ var letter = String.fromCharCode(keyCode).toUpperCase(); // Handle Start / Next / Submit - if (keyCode === 13) { // Enter : go Next + if (keyCode === 13 || keyCode === 39) { // Enter or arrow-right: go Next event.preventDefault(); if (!this.preventEnterSubmit) { var isFinish = this.$('button[value="finish"]').length !== 0; this._submitForm({isFinish: isFinish}); } + } else if (keyCode === 37) { // arrow-left: previous (if available) + // It's easier to actually click on the button (if in the DOM) as it contains necessary + // data that are used in the event handler. + // Again, global selector necessary since the navigation is outside of the form. + $('.o_survey_navigation_submit[value="previous"]').click(); } else if (self.options.questionsLayout === 'page_per_question' && letter.match(/[a-z]/i)) { var $choiceInput = this.$(`input[data-selection-key=${letter}]`); @@ -416,6 +432,11 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({ this.$surveyProgress.remove(); } + if (result.survey_navigation && this.$surveyNavigation.length !== 0) { + this.$surveyNavigation.html(result.survey_navigation); + this.$surveyNavigation.find('.o_survey_navigation_submit').on('click', self._onSubmit.bind(self)); + } + // Hide timer if end screen (if page_per_question in case of conditional questions) if (self.options.questionsLayout === 'page_per_question' && this.$('.o_survey_finished').length > 0) { options.isFinish = true; diff --git a/addons/survey/static/src/scss/survey_form.scss b/addons/survey/static/src/scss/survey_form.scss index 1bc4cc80c70..97b1343b2fe 100644 --- a/addons/survey/static/src/scss/survey_form.scss +++ b/addons/survey/static/src/scss/survey_form.scss @@ -27,6 +27,10 @@ nav#oe_main_menu_navbar { } } +.o_survey_navigation_wrapper .o_survey_navigation_submit { + cursor: pointer; +} + .o_survey_brand_message { background-color: rgba(255,255,255,0.7); } diff --git a/addons/survey/views/survey_templates.xml b/addons/survey/views/survey_templates.xml index c987eac5f8a..c26189a31ce 100644 --- a/addons/survey/views/survey_templates.xml +++ b/addons/survey/views/survey_templates.xml @@ -35,11 +35,13 @@ -
+
+
+
@@ -183,8 +185,6 @@
- or press Enter
@@ -220,15 +220,6 @@
- -