From 7c0423ff03a9e414e9fed6e3be32d92ba2679ce2 Mon Sep 17 00:00:00 2001 From: Matthew Piatetsky <mpiatetsky@edx.org> Date: Tue, 22 Aug 2017 16:33:05 -0400 Subject: [PATCH] Revert revert of unenrollment survey. This reverts commit e2ee48c3c9af8a324e18c8347cd9488e473bcb43. --- .../courseware/features/registration.feature | 10 --- lms/static/js/dashboard/legacy.js | 14 ---- .../learner_dashboard/unenrollment_factory.js | 13 ++++ .../learner_dashboard/views/unenroll_view.js | 78 +++++++++++++++++++ .../learner_dashboard/unenroll_view_spec.js | 46 +++++++++++ lms/static/lms/js/build.js | 1 + lms/static/lms/js/spec/main.js | 1 + lms/static/sass/multicourse/_dashboard.scss | 52 +++++++++++++ lms/templates/dashboard.html | 13 +++- .../dashboard/_dashboard_course_listing.html | 1 + lms/templates/dashboard/_reason_survey.html | 37 +++++++++ themes/edx.org/lms/templates/dashboard.html | 13 +++- 12 files changed, 253 insertions(+), 26 deletions(-) create mode 100644 lms/static/js/learner_dashboard/unenrollment_factory.js create mode 100644 lms/static/js/learner_dashboard/views/unenroll_view.js create mode 100644 lms/static/js/spec/learner_dashboard/unenroll_view_spec.js create mode 100644 lms/templates/dashboard/_reason_survey.html diff --git a/lms/djangoapps/courseware/features/registration.feature b/lms/djangoapps/courseware/features/registration.feature index fe270a3f857..60b094de48d 100644 --- a/lms/djangoapps/courseware/features/registration.feature +++ b/lms/djangoapps/courseware/features/registration.feature @@ -11,13 +11,3 @@ Feature: LMS.Register for a course When I register for the course "6.002x" Then I should see the course numbered "6.002x" in my dashboard And a "edx.course.enrollment.activated" server event is emitted - - Scenario: I can unenroll from a course - Given I am registered for the course "6.002x" - And I visit the dashboard - Then I should see the course numbered "6.002x" in my dashboard - When I unenroll from the course numbered "6.002x" - Then I should be on the dashboard page - And I should see an empty dashboard message - And I should NOT see the course numbered "6.002x" in my dashboard - And a "edx.course.enrollment.deactivated" server event is emitted diff --git a/lms/static/js/dashboard/legacy.js b/lms/static/js/dashboard/legacy.js index 80402ec5198..a2c3a4a4db1 100644 --- a/lms/static/js/dashboard/legacy.js +++ b/lms/static/js/dashboard/legacy.js @@ -191,20 +191,6 @@ $('#unenroll-modal').css('position', 'fixed'); }); - $('#unenroll_form').on('ajax:complete', function(event, xhr) { - if (xhr.status === 200) { - location.href = urls.dashboard; - } else if (xhr.status === 403) { - location.href = urls.signInUser + '?course_id=' + - encodeURIComponent($('#unenroll_course_id').val()) + '&enrollment_action=unenroll'; - } else { - $('#unenroll_error').text( - xhr.responseText ? xhr.responseText : gettext('An error occurred. Please try again later.') - ).stop() - .css('display', 'block'); - } - }); - $('#email_settings_form').submit(function() { $.ajax({ type: 'POST', diff --git a/lms/static/js/learner_dashboard/unenrollment_factory.js b/lms/static/js/learner_dashboard/unenrollment_factory.js new file mode 100644 index 00000000000..4afca20bbb4 --- /dev/null +++ b/lms/static/js/learner_dashboard/unenrollment_factory.js @@ -0,0 +1,13 @@ +(function(define) { + 'use strict'; + + define([ + 'js/learner_dashboard/views/unenroll_view' + ], + function(UnenrollView) { + return function(options) { + var Unenroll = new UnenrollView(options); + return Unenroll; + }; + }); +}).call(this, define || RequireJS.define); diff --git a/lms/static/js/learner_dashboard/views/unenroll_view.js b/lms/static/js/learner_dashboard/views/unenroll_view.js new file mode 100644 index 00000000000..ab52dfe83eb --- /dev/null +++ b/lms/static/js/learner_dashboard/views/unenroll_view.js @@ -0,0 +1,78 @@ +(function(define) { + 'use strict'; + define(['backbone', + 'jquery', + 'underscore', + 'gettext', + 'edx-ui-toolkit/js/utils/html-utils' + ], + function( + Backbone, + $, + _, + gettext, + HtmlUtils + ) { + return Backbone.View.extend({ + el: '.unenroll-modal', + + switchToSlideOne: function() { + var survey, i, + reasonsSurvey = HtmlUtils.HTML($('.reasons_survey')); + // Randomize survey option order + survey = document.querySelector('.options'); + for (i = survey.children.length - 1; i >= 0; i--) { + survey.appendChild(survey.children[Math.random() * i | 0]); + } + $('.inner-wrapper header').hide(); + $('#unenroll_form').after(HtmlUtils.ensureHtml(reasonsSurvey).toString()).hide(); + $('.reasons_survey .slide1').removeClass('hidden'); + }, + + switchToSlideTwo: function() { + var reason = $(".reasons_survey input[name='reason']:checked").attr('val'); + if (reason === 'Other') { + reason = $('.other_text').val(); + } + if (reason) { + window.analytics.track('unenrollment_reason.selected', { + category: 'user-engagement', + label: reason, + displayName: 'v1' + }); + } + HtmlUtils.setHtml($('.reasons_survey'), HtmlUtils.HTML($('.slide2').html())); + $('.reasons_survey .return_to_dashboard').attr('href', this.urls.dashboard); + $('.reasons_survey .browse_courses').attr('href', this.urls.browseCourses); + }, + + unenrollComplete: function(event, xhr) { + if (xhr.status === 200) { + if (!this.isEdx) { + location.href = this.urls.dashboard; + } else { + this.switchToSlideOne(); + $('.submit_reasons').click(this.switchToSlideTwo.bind(this)); + } + } else if (xhr.status === 403) { + location.href = this.urls.signInUser + '?course_id=' + + encodeURIComponent($('#unenroll_course_id').val()) + '&enrollment_action=unenroll'; + } else { + $('#unenroll_error').text( + gettext('Unable to determine whether we should give you a refund because' + + ' of System Error. Please try again later.') + ).stop() + .css('display', 'block'); + } + }, + + initialize: function(options) { + this.urls = options.urls; + this.isEdx = options.isEdx; + + $('#unenroll_form').on('ajax:complete', this.unenrollComplete.bind(this)); + } + }); + } + ); +}).call(this, define || RequireJS.define); diff --git a/lms/static/js/spec/learner_dashboard/unenroll_view_spec.js b/lms/static/js/spec/learner_dashboard/unenroll_view_spec.js new file mode 100644 index 00000000000..1aa0c8efc99 --- /dev/null +++ b/lms/static/js/spec/learner_dashboard/unenroll_view_spec.js @@ -0,0 +1,46 @@ +define([ + 'backbone', + 'js/learner_dashboard/views/unenroll_view' +], function(Backbone, UnenrollView) { + 'use strict'; + + describe('Unenroll View', function() { + var view = null, + options = { + urls: { + dashboard: '/dashboard', + browseCourses: '/courses' + }, + isEdx: true + }, + initView; + + initView = function() { + return new UnenrollView(options); + }; + + beforeEach(function() { + setFixtures('<div class="wrapper-action-more" data-course-key="course-v1:edX+DemoX+Demo_Course"> <button type="button" class="action action-more" id="actions-dropdown-link-0" aria-haspopup="true" aria-expanded="true" aria-controls="actions-dropdown-0" data-course-number="DemoX" data-course-name="edX Demonstration Course" data-dashboard-index="0"> <span class="sr">Course options for</span> <span class="sr"> edX Demonstration Course </span> <span class="fa fa-cog" aria-hidden="true"></span> </button> <div class="actions-dropdown is-visible" id="actions-dropdown-0" tabindex="-1"> <ul class="actions-dropdown-list" id="actions-dropdown-list-0" aria-label="Available Actions" role="menu"> <div class="reasons_survey"> <div class="slide1 hidden"> <h3>We\'re sorry to see you go! Please share your main reason for unenrolling.</h3><br> <ul class="options"> <li><label class="option"><input type="radio" name="reason" val="I don\'t have enough support">I don\'t have enough support</label></li><li><label class="option"><input type="radio" name="reason" val="I don’t have the academic or language prerequisites">I don\'t have the academic or language prerequisites</label></li><li><label class="option"><input type="radio" name="reason" val="Something was broken">Something was broken</label></li><li><label class="option"><input type="radio" name="reason" val="I just wanted to browse the material">I just wanted to browse the material</label></li><li><label class="option"><input type="radio" name="reason" val="This won’t help me reach my goals">This won\'t help me reach my goals</label></li><li><label class="option"><input type="radio" name="reason" val="I am not happy with the quality of the content">I am not happy with the quality of the content</label></li><li><label class="option"><input type="radio" name="reason" val="The course material was too hard">The course material was too hard</label></li><li><label class="option"><input type="radio" name="reason" val="I don\'t have the time">I don\'t have the time</label></li><li><label class="option"><input type="radio" name="reason" val="The course material was too easy">The course material was too easy</label></li><li><label class="option"><input class="other_radio" type="radio" name="reason" val="Other">Other <input type="text" class="other_text"></label></li></ul> <button class="submit_reasons">Submit</button> </div> </div> <div class="slide2 hidden"> Thank you for sharing your reasons for unenrolling.<br> You are unenrolled from edX Demonstration Course. <a class="button survey_button return_to_dashboard"> Return To Dashboard </a> <a class="button survey_button browse_courses"> Browse Courses </a> </div> <li class="actions-item" id="actions-item-unenroll-0"> <a href="#unenroll-modal" class="action action-unenroll" rel="leanModal" data-course-id="course-v1:edX+DemoX+Demo_Course" data-course-number="DemoX" data-course-name="edX Demonstration Course" data-dashboard-index="0" data-track-info="Are you sure you want to unenroll from %(course_name)s (%(course_number)s)?" id="unenroll-0"> Unenroll </a> </li> <li class="actions-item" id="actions-item-email-settings-0"> </li> </ul> </div> </div>'); // eslint-disable-line max-len + }); + + afterEach(function() { + view.remove(); + }); + + it('should exist', function() { + view = initView(); + expect(view).toBeDefined(); + }); + + it('switch between slides', function() { + view = initView(); + expect($('.slide1').hasClass('hidden')).toEqual(true); + view.switchToSlideOne(); + expect($('.slide1').hasClass('hidden')).toEqual(false); + expect($('.slide2').hasClass('hidden')).toEqual(true); + view.switchToSlideTwo(); + expect($('.slide2').hasClass('hidden')).toEqual(true); + }); + }); +} +); diff --git a/lms/static/lms/js/build.js b/lms/static/lms/js/build.js index c2141007e05..b4a74f5a52c 100644 --- a/lms/static/lms/js/build.js +++ b/lms/static/lms/js/build.js @@ -32,6 +32,7 @@ 'js/groups/views/cohorts_dashboard_factory', 'js/discussions_management/views/discussions_dashboard_factory', 'js/header_factory', + 'js/learner_dashboard/unenrollment_factory', 'js/learner_dashboard/program_details_factory', 'js/learner_dashboard/program_list_factory', 'js/student_account/logistration_factory', diff --git a/lms/static/lms/js/spec/main.js b/lms/static/lms/js/spec/main.js index e377f28e31b..954a9626068 100644 --- a/lms/static/lms/js/spec/main.js +++ b/lms/static/lms/js/spec/main.js @@ -759,6 +759,7 @@ 'js/spec/learner_dashboard/program_details_header_spec.js', 'js/spec/learner_dashboard/program_details_view_spec.js', 'js/spec/learner_dashboard/program_details_sidebar_view_spec.js', + 'js/spec/learner_dashboard/unenroll_view_spec.js', 'js/spec/learner_dashboard/course_card_view_spec.js', 'js/spec/learner_dashboard/course_enroll_view_spec.js', 'js/spec/markdown_editor_spec.js', diff --git a/lms/static/sass/multicourse/_dashboard.scss b/lms/static/sass/multicourse/_dashboard.scss index 0b3c9e4752b..d27b5be8af8 100644 --- a/lms/static/sass/multicourse/_dashboard.scss +++ b/lms/static/sass/multicourse/_dashboard.scss @@ -1498,3 +1498,55 @@ a.fade-cover{ } } } + +#unenroll-modal { + margin-top: -60px; +} + +.reasons_survey { + padding: 20px; + + .options { + list-style: none; + padding: 0; + } + + .option { + margin-bottom: 10px; + display: block; + } + + input { + margin-right: 10px; + } + + .unenroll-header { + background-image: none; + } + + .other_text { + margin-top: 0; + } + + .other_radio { + margin-top: 10px; + } + + .submit_reasons { + margin-top: 10px; + } + + .survey_button { + width: 30%; + margin-top: 10px; + margin-left: 2.5%; + margin-right: 2.5%; + color: $white; + } + + .survey_button:visited, .survey_button:hover, .survey_button:focus { + color: $white; + text-decoration: none; + } + +} diff --git a/lms/templates/dashboard.html b/lms/templates/dashboard.html index 552d815e3b1..13765bdd021 100644 --- a/lms/templates/dashboard.html +++ b/lms/templates/dashboard.html @@ -42,6 +42,17 @@ from openedx.core.djangolib.markup import HTML, Text }); }); </script> + <%static:require_module module_name="js/learner_dashboard/unenrollment_factory" class_name="UnenrollmentFactory"> + UnenrollmentFactory({ + urls: { + dashboard: "${reverse('dashboard') | n, js_escaped_string}", + signInUser: "${reverse('signin_user') | n, js_escaped_string}", + changeEmailSettings: "${reverse('change_email_settings') | n, js_escaped_string}", + browseCourses: "${marketing_link('COURSES') | n, js_escaped_string}" + }, + isEdx: false + }); + </%static:require_module> % if settings.FEATURES.get('ENABLE_DASHBOARD_SEARCH'): <%static:require_module module_name="course_search/js/dashboard_search_factory" class_name="DashboardSearchFactory"> DashboardSearchFactory(); @@ -244,7 +255,7 @@ from openedx.core.djangolib.markup import HTML, Text </span> </button> - <header> + <header class="unenroll-header"> <h2 id="unenrollment-modal-title"> <span id='track-info'></span> <span id='refund-info'></span> diff --git a/lms/templates/dashboard/_dashboard_course_listing.html b/lms/templates/dashboard/_dashboard_course_listing.html index ec2874228d2..943d6fd347c 100644 --- a/lms/templates/dashboard/_dashboard_course_listing.html +++ b/lms/templates/dashboard/_dashboard_course_listing.html @@ -220,6 +220,7 @@ from util.course import get_link_for_about_page, get_encoded_course_sharing_utm_ <div class="actions-dropdown" id="actions-dropdown-${dashboard_index}" tabindex="-1"> <ul class="actions-dropdown-list" id="actions-dropdown-list-${dashboard_index}" aria-label="${_('Available Actions')}" role="menu"> % if can_unenroll: + <%include file='_reason_survey.html' args='course_overview=course_overview' /> <li class="actions-item" id="actions-item-unenroll-${dashboard_index}"> <% course_refund_url = reverse('course_run_refund_status', args=[unicode(course_overview.id)]) %> % if not is_course_blocked: diff --git a/lms/templates/dashboard/_reason_survey.html b/lms/templates/dashboard/_reason_survey.html new file mode 100644 index 00000000000..b0fffb0b54f --- /dev/null +++ b/lms/templates/dashboard/_reason_survey.html @@ -0,0 +1,37 @@ +<%page args="course_overview" expression_filter="h"/> +<%! +import urllib + +from django.utils.translation import ugettext as _ +%> +<div class="reasons_survey"> + <div class="slide1 hidden"> + <h3>${_("We're sorry to see you go! Please share your main reason for unenrolling.")}</h3><br> + <ul class="options"> + <li><label class="option"><input type="radio" name="reason" val="I just wanted to browse the material">${_('I just wanted to browse the material')}</label></li> + <li><label class="option"><input type="radio" name="reason" val="This won’t help me reach my goals">${_("This won't help me reach my goals")}</label></li> + <li><label class="option"><input type="radio" name="reason" val="I don't have the time">${_("I don't have the time")}</label></li> + <li><label class="option"><input type="radio" name="reason" val="I don’t have the academic or language prerequisites">${_("I don't have the academic or language prerequisites")}</label></li> + <li><label class="option"><input type="radio" name="reason" val="I don't have enough support">${_("I don't have enough support")}</label></li> + <li><label class="option"><input type="radio" name="reason" val="I am not happy with the quality of the content">${_('I am not happy with the quality of the content')}</label></li> + <li><label class="option"><input type="radio" name="reason" val="The course material was too hard">${_('The course material was too hard')}</label></li> + <li><label class="option"><input type="radio" name="reason" val="The course material was too easy">${_('The course material was too easy')}</label></li> + <li><label class="option"><input type="radio" name="reason" val="Something was broken">${_('Something was broken')}</label></li> + <li><label class="option"><input class="other_radio" type="radio" name="reason" val="Other">${_('Other')} <input type="text" class="other_text"/></label></li> + </ul> + <button class="submit_reasons">${_('Submit')}</button> + </div> +</div> +<div class="slide2 hidden"> + ${_('Thank you for sharing your reasons for unenrolling.')}<br> + ${_('You are unenrolled from')} ${course_overview.display_name_with_default}. + + <div> + <a class="button survey_button return_to_dashboard"> + ${_('Return To Dashboard')} + </a> + <a class="button survey_button browse_courses"> + ${_('Browse Courses')} + </a> + </div> +</div> diff --git a/themes/edx.org/lms/templates/dashboard.html b/themes/edx.org/lms/templates/dashboard.html index e6d9ee4bd62..deaf0cb4844 100644 --- a/themes/edx.org/lms/templates/dashboard.html +++ b/themes/edx.org/lms/templates/dashboard.html @@ -43,6 +43,17 @@ from openedx.core.djangoapps.theming import helpers as theming_helpers }); }); </script> + <%static:require_module module_name="js/learner_dashboard/unenrollment_factory" class_name="UnenrollmentFactory"> + UnenrollmentFactory({ + urls: { + dashboard: "${reverse('dashboard') | n, js_escaped_string}", + signInUser: "${reverse('signin_user') | n, js_escaped_string}", + changeEmailSettings: "${reverse('change_email_settings') | n, js_escaped_string}", + browseCourses: "${marketing_link('COURSES') | n, js_escaped_string}" + }, + isEdx: true + }); + </%static:require_module> % if settings.FEATURES.get('ENABLE_DASHBOARD_SEARCH'): <%static:require_module module_name="course_search/js/dashboard_search_factory" class_name="DashboardSearchFactory"> DashboardSearchFactory(); @@ -258,7 +269,7 @@ from openedx.core.djangoapps.theming import helpers as theming_helpers </span> </button> - <header> + <header class="unenroll-header"> <h2 id="unenrollment-modal-title"> <span id='track-info'></span> <span id='refund-info'></span> -- GitLab