From 939a3672d4c2f65a6c8d04ebe7cacb621d8a1485 Mon Sep 17 00:00:00 2001 From: marjev Date: Fri, 27 Mar 2015 10:59:22 +0000 Subject: [PATCH] (SOL-531)(SOL-532)Course Cards update; A link to course discovery page has been added to the OpenEdX homepage --- lms/djangoapps/branding/tests/test_page.py | 28 +-- lms/djangoapps/courseware/courses.py | 6 +- lms/envs/common.py | 2 +- .../sass/application-extend2-rtl.scss.mako | 1 + lms/static/sass/application-extend2.scss.mako | 1 + lms/static/sass/base/_grid-settings.scss | 11 +- lms/static/sass/shared/_course_object.scss | 17 -- lms/static/sass/shared/_footer.scss | 4 +- lms/static/sass/shared/_header.scss | 4 +- lms/static/sass/views/_homepage.scss | 163 ++++++++++++++++++ lms/static/sass/views/_verification.scss | 4 +- lms/templates/course.html | 49 +++--- lms/templates/courseware/courses.html | 4 +- lms/templates/index.html | 8 +- 14 files changed, 227 insertions(+), 75 deletions(-) create mode 100644 lms/static/sass/views/_homepage.scss diff --git a/lms/djangoapps/branding/tests/test_page.py b/lms/djangoapps/branding/tests/test_page.py index 745f3e4f36..dabcc6f8e0 100644 --- a/lms/djangoapps/branding/tests/test_page.py +++ b/lms/djangoapps/branding/tests/test_page.py @@ -202,9 +202,9 @@ class IndexPageCourseCardsSortingTests(ModuleStoreTestCase): ((template, context), _) = RENDER_MOCK.call_args # pylint: disable=unpacking-non-sequence self.assertEqual(template, 'index.html') - # Now the courses will be stored in their announcement dates. - self.assertEqual(context['courses'][0].id, self.starting_later.id) - self.assertEqual(context['courses'][1].id, self.starting_earlier.id) + # by default the courses will be sorted by their creation dates, earliest first. + self.assertEqual(context['courses'][0].id, self.starting_earlier.id) + self.assertEqual(context['courses'][1].id, self.starting_later.id) self.assertEqual(context['courses'][2].id, self.course_with_default_start_date.id) # check the /courses view @@ -213,23 +213,23 @@ class IndexPageCourseCardsSortingTests(ModuleStoreTestCase): ((template, context), _) = RENDER_MOCK.call_args # pylint: disable=unpacking-non-sequence self.assertEqual(template, 'courseware/courses.html') - # Now the courses will be stored in their announcement dates. - self.assertEqual(context['courses'][0].id, self.starting_later.id) - self.assertEqual(context['courses'][1].id, self.starting_earlier.id) + # by default the courses will be sorted by their creation dates, earliest first. + self.assertEqual(context['courses'][0].id, self.starting_earlier.id) + self.assertEqual(context['courses'][1].id, self.starting_later.id) self.assertEqual(context['courses'][2].id, self.course_with_default_start_date.id) @patch('student.views.render_to_response', RENDER_MOCK) @patch('courseware.views.render_to_response', RENDER_MOCK) - @patch.dict('django.conf.settings.FEATURES', {'ENABLE_COURSE_SORTING_BY_START_DATE': True}) - def test_course_cards_sorted_by_start_date_show_earliest_first(self): + @patch.dict('django.conf.settings.FEATURES', {'ENABLE_COURSE_SORTING_BY_START_DATE': False}) + def test_course_cards_sorted_by_start_date_disabled(self): response = self.client.get('/') self.assertEqual(response.status_code, 200) ((template, context), _) = RENDER_MOCK.call_args # pylint: disable=unpacking-non-sequence self.assertEqual(template, 'index.html') - # now the courses will be sorted by their creation dates, earliest first. - self.assertEqual(context['courses'][0].id, self.starting_earlier.id) - self.assertEqual(context['courses'][1].id, self.starting_later.id) + # now the courses will be sorted by their announcement dates. + self.assertEqual(context['courses'][0].id, self.starting_later.id) + self.assertEqual(context['courses'][1].id, self.starting_earlier.id) self.assertEqual(context['courses'][2].id, self.course_with_default_start_date.id) # check the /courses view as well @@ -238,7 +238,7 @@ class IndexPageCourseCardsSortingTests(ModuleStoreTestCase): ((template, context), _) = RENDER_MOCK.call_args # pylint: disable=unpacking-non-sequence self.assertEqual(template, 'courseware/courses.html') - # now the courses will be sorted by their creation dates, earliest first. - self.assertEqual(context['courses'][0].id, self.starting_earlier.id) - self.assertEqual(context['courses'][1].id, self.starting_later.id) + # now the courses will be sorted by their announcement dates. + self.assertEqual(context['courses'][0].id, self.starting_later.id) + self.assertEqual(context['courses'][1].id, self.starting_earlier.id) self.assertEqual(context['courses'][2].id, self.course_with_default_start_date.id) diff --git a/lms/djangoapps/courseware/courses.py b/lms/djangoapps/courseware/courses.py index 28dbbf9b34..192ba1943f 100644 --- a/lms/djangoapps/courseware/courses.py +++ b/lms/djangoapps/courseware/courses.py @@ -383,7 +383,11 @@ def sort_by_start_date(courses): """ Returns a list of courses sorted by their start date, latest first. """ - courses = sorted(courses, key=lambda course: (course.start is None, course.start), reverse=False) + courses = sorted( + courses, + key=lambda course: (course.has_ended(), course.start is None, course.start), + reverse=False + ) return courses diff --git a/lms/envs/common.py b/lms/envs/common.py index abdfdbd01a..c8054ed094 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -308,7 +308,7 @@ FEATURES = { # When a user goes to the homepage ('/') the user see the # courses listed in the announcement dates order - this is default Open edX behavior. # Set to True to change the course sorting behavior by their start dates, latest first. - 'ENABLE_COURSE_SORTING_BY_START_DATE': False, + 'ENABLE_COURSE_SORTING_BY_START_DATE': True, # Flag to enable new user account APIs. 'ENABLE_USER_REST_API': False, diff --git a/lms/static/sass/application-extend2-rtl.scss.mako b/lms/static/sass/application-extend2-rtl.scss.mako index 8c3fc33ac0..89ba77d76c 100644 --- a/lms/static/sass/application-extend2-rtl.scss.mako +++ b/lms/static/sass/application-extend2-rtl.scss.mako @@ -47,6 +47,7 @@ @import 'views/verification'; @import 'views/shoppingcart'; @import 'views/login-register'; +@import 'views/homepage'; // applications @import "discussion/utilities/variables"; diff --git a/lms/static/sass/application-extend2.scss.mako b/lms/static/sass/application-extend2.scss.mako index 5969c25e8c..e290603a4a 100644 --- a/lms/static/sass/application-extend2.scss.mako +++ b/lms/static/sass/application-extend2.scss.mako @@ -49,6 +49,7 @@ @import 'views/verification'; @import 'views/decoupled-verification'; @import 'views/shoppingcart'; +@import 'views/homepage'; @import 'course/auto-cert'; // applications diff --git a/lms/static/sass/base/_grid-settings.scss b/lms/static/sass/base/_grid-settings.scss index 582472519e..3ae64b02ee 100644 --- a/lms/static/sass/base/_grid-settings.scss +++ b/lms/static/sass/base/_grid-settings.scss @@ -4,10 +4,13 @@ $max-width: 1200px; /* Override the default global box-sizing */ $border-box-sizing: false; +/* Grid width variables */ +$large-min-width: 769px; /* Breakpoints */ -$mobile: new-breakpoint(max-width 320px 4); -$tablet: new-breakpoint(min-width 321px max-width 768px, 8); -$desktop: new-breakpoint(min-width 769px 12); -$xl-desktop: new-breakpoint(min-width 980px 12); +$bp-tiny: new-breakpoint(max-width 320px 4); +$bp-small: new-breakpoint(min-width 321px max-width 540px, 4); +$bp-medium: new-breakpoint(min-width 541px max-width 768px, 8); +$bp-large: new-breakpoint(min-width $large-min-width max-width 979px, 12); +$bp-huge: new-breakpoint(min-width 980px 12); diff --git a/lms/static/sass/shared/_course_object.scss b/lms/static/sass/shared/_course_object.scss index 8910164bc4..ade6464c51 100644 --- a/lms/static/sass/shared/_course_object.scss +++ b/lms/static/sass/shared/_course_object.scss @@ -13,23 +13,6 @@ } } - .courses-listing { - @include clearfix(); - margin: 0; - padding: 0; - list-style: none; - - .courses-listing-item { - width: flex-grid(4); - @include margin-right(flex-gutter()); - @include float(left); - - &:nth-child(3n+3) { - @include margin-right(0); - } - } - } - .course { background: $body-bg; border: 1px solid $border-color-1; diff --git a/lms/static/sass/shared/_footer.scss b/lms/static/sass/shared/_footer.scss index e40c21f1f1..00da22a609 100644 --- a/lms/static/sass/shared/_footer.scss +++ b/lms/static/sass/shared/_footer.scss @@ -446,10 +446,10 @@ $edx-footer-bg-color: rgb(252,252,252); @include span-columns(12); } - @include media( $tablet ) { + @include media( $bp-medium ) { } - @include media( $desktop ) { + @include media( $bp-large ) { .footer-about { @include span-columns(6); } diff --git a/lms/static/sass/shared/_header.scss b/lms/static/sass/shared/_header.scss index 45e7bc30c4..907207fae0 100644 --- a/lms/static/sass/shared/_header.scss +++ b/lms/static/sass/shared/_header.scss @@ -765,7 +765,7 @@ header.global-new { } } - @include media( $desktop ) { + @include media( $bp-large ) { .wrapper-header { width: 100%; min-width: 800px; @@ -828,7 +828,7 @@ header.global-new { } } - @include media( $xl-desktop ) { + @include media( $bp-huge ) { .wrapper-header { padding: 17px 0; } diff --git a/lms/static/sass/views/_homepage.scss b/lms/static/sass/views/_homepage.scss new file mode 100644 index 0000000000..fef6b17f4c --- /dev/null +++ b/lms/static/sass/views/_homepage.scss @@ -0,0 +1,163 @@ +// lms - views - homepage view +// ==================== + +$course-card-height: ($baseline*18); +$course-image-height: ($baseline*8); +$course-info-height: ($baseline*10); +$course-title-height: ($baseline*3.6); +$learn-more-horizontal-position: calc(50% - 100px); // calculate the left position for "LEARN MORE" content + +.courses-container { + @include outer-container; + padding: ($baseline*0.9) ($baseline/2) 0 ($baseline/2); + + .courses { + @include row(); + + .courses-listing { + @extend %ui-no-list; + + .courses-listing-item { + @include fill-parent(); + max-height: $course-card-height; + margin: ($baseline*0.75) 0 ($baseline*1.5) 0; + + @include media($bp-medium) { + @include span-columns(4); // 4 of 8 + @include omega(2n); + } + + @include media($bp-large) { + @include span-columns(4); // 4 of 12 + @include omega(3n); + } + + @include media($bp-huge) { + @include span-columns(3); // 3 of 12 + @include omega(4n); + } + } + } + + .course { + @include box-sizing(border-box); + @include transition(all $tmg-f3 linear 0s); + position: relative; + border-bottom: 3px solid $action-primary-bg; + box-shadow: 0 1px 10px 0 $black-t0, inset 0 0 0 1px $white-t3; + background: $body-bg; + width: 100%; + + .course-image .cover-image { + height: $course-image-height; + overflow: hidden; + + // places the shadow on top of the course image while hovering over the course card + &:before { + @include left(0); + @extend %ui-depth1; + position: absolute; + top: 0; + opacity: 0; + background: $black; + width: 100%; + height: $course-image-height; + content: ''; + } + + img { + width: 100%; + height: auto; + } + + .learn-more { + @include left($learn-more-horizontal-position); + @include box-sizing(border-box); + @include line-height(28); + @extend %ui-depth2; + position: absolute; + top: ($baseline*2.75); + opacity: 0; + border: 3px solid $white; + border-radius: 3px; + padding: 0 $baseline; + width: ($baseline*10); + height: ($baseline*2.5); + text-align: center; + color: $white; + } + } + + .course-info { + height: $course-info-height; + + .course-organization, .course-code, .course-date { + @extend %t-icon6; + color: $black; + } + + .course-organization, .course-code, .course-title { + display: block; + } + + .course-organization { + @include line-height(11); + padding: ($baseline/2) ($baseline*0.75) ($baseline/10) ($baseline*0.75); + } + + .course-code { + @include line-height(16); + padding: 0 ($baseline*0.75); + } + + .course-title { + @include line-height(16); + @extend %t-icon4; + margin: ($baseline*0.25) 0 ($baseline*1.75) 0; + padding: 0 ($baseline*0.75); + height: $course-title-height; + color: $link-color; + } + + .course-date { + @include line-height(14); + padding: ($baseline/10) ($baseline*0.75); + } + } + + // STATE: hover and focus + &:hover, + &:focus { + .cover-image { + &:before { + @include transition(opacity $tmg-f2 ease-out $tmg-f2); + opacity: 0.6; + } + + .learn-more { + @include transition(opacity $tmg-f2 ease-out $tmg-f2); + opacity: 1; + } + } + } + } + } + + .courses-more { + @include margin-right(0); + text-align: center; + + @include media($large-min-width) { + @include margin-right($baseline*0.5); + @include text-align(right); + } + + .courses-more-cta { + font-weight: $font-semibold; + + &:after { + content: " ›"; + } + } + } +} diff --git a/lms/static/sass/views/_verification.scss b/lms/static/sass/views/_verification.scss index 9549b2093a..b047c9995b 100644 --- a/lms/static/sass/views/_verification.scss +++ b/lms/static/sass/views/_verification.scss @@ -1420,7 +1420,7 @@ } } - @include media( $desktop ) { + @include media( $bp-large ) { .contribution-options { .field { width: auto; @@ -1443,7 +1443,7 @@ } } - @include media( $xl-desktop ) { + @include media( $bp-huge ) { .register-choice { .list-actions { float: right; diff --git a/lms/templates/course.html b/lms/templates/course.html index a67f157662..939bccf25e 100644 --- a/lms/templates/course.html +++ b/lms/templates/course.html @@ -5,35 +5,28 @@ from django.core.urlresolvers import reverse from courseware.courses import course_image_url, get_course_about_section %> <%page args="course" /> -
- %if course.is_newish: - ${_("New")} - %endif +
-
-
-
-

${course.display_number_with_default | h} ${get_course_about_section(course, 'title')}

-
- -
-
-
- ${course.display_number_with_default | h} ${get_course_about_section(course, 'title')} Cover Image -
-
-

${get_course_about_section(course, 'short_description')}

-
-
- ${get_course_about_section(course, 'university')} - % if not course.start_date_is_still_default: - ${course.start_datetime_text()} - % endif -
-
-
-
diff --git a/lms/templates/courseware/courses.html b/lms/templates/courseware/courses.html index 3c65a86585..bd19899780 100644 --- a/lms/templates/courseware/courses.html +++ b/lms/templates/courseware/courses.html @@ -1,4 +1,4 @@ -<%! +<%! from django.utils.translation import ugettext as _ from microsite_configuration import microsite %> @@ -44,7 +44,7 @@ -
+
    %for course in courses: diff --git a/lms/templates/index.html b/lms/templates/index.html index 2e9d094df8..1b1612b636 100644 --- a/lms/templates/index.html +++ b/lms/templates/index.html @@ -48,19 +48,23 @@ % endif -
    +
    % if settings.FEATURES.get('COURSES_ARE_BROWSABLE'):
      - %for course in courses: + ## cap for showing 9 or less courses + %for course in courses[:9]:
    • <%include file="course.html" args="course=course" />
    • %endfor
    + % endif