diff --git a/cms/api.py b/cms/api.py index 3b32fedcaa..093e23605f 100644 --- a/cms/api.py +++ b/cms/api.py @@ -9,7 +9,11 @@ from wagtail.models import Page, Site from cms import models as cms_models -from cms.constants import CERTIFICATE_INDEX_SLUG, ENTERPRISE_PAGE_SLUG, CatalogSorting +from cms.constants import ( + CERTIFICATE_INDEX_SLUG, + ENTERPRISE_PAGE_SLUG, + CatalogSorting, +) log = logging.getLogger(__name__) DEFAULT_HOMEPAGE_PROPS = dict(title="Home Page", subhead="This is the home page") # noqa: C408 diff --git a/cms/constants.py b/cms/constants.py index ec99d74d6b..1a571ca302 100644 --- a/cms/constants.py +++ b/cms/constants.py @@ -12,6 +12,7 @@ COMMON_COURSEWARE_COMPONENT_INDEX_SLUG = "common-courseware-component-pages" ALL_TOPICS = "All Topics" +ALL_LANGUAGES = "All Languages" ALL_TAB = "all-tab" # ************** CONSTANTS FOR WEBINARS ************** diff --git a/cms/models.py b/cms/models.py index cffaac70a8..df7704a4a2 100644 --- a/cms/models.py +++ b/cms/models.py @@ -61,6 +61,7 @@ from cms.constants import ( ALL_TAB, ALL_TOPICS, + ALL_LANGUAGES, BLOG_INDEX_SLUG, CERTIFICATE_INDEX_SLUG, COMMON_COURSEWARE_COMPONENT_INDEX_SLUG, @@ -90,8 +91,11 @@ Program, ProgramCertificate, ProgramRun, + CourseLanguage, ) from ecommerce.models import Product +from mitol.olposthog.features import is_enabled +from mitxpro.features import CATALOG_LANGUAGE_FILTER from mitxpro.utils import now_in_utc from mitxpro.views import get_base_context @@ -516,6 +520,9 @@ def get_context(self, request, *args, **kwargs): # noqa: ARG002 Populate the context with live programs, courses and programs + courses """ topic_filter = request.GET.get("topic", ALL_TOPICS) + language_filter = request.GET.get("language", ALL_LANGUAGES) + + is_language_filter_enabled = is_enabled(CATALOG_LANGUAGE_FILTER, default=False) # Best Match is the default sorting. sort_by = request.GET.get("sort-by", CatalogSorting.BEST_MATCH.sorting_value) @@ -556,10 +563,19 @@ def get_context(self, request, *args, **kwargs): # noqa: ARG002 .order_by("title") ) + if language_filter != ALL_LANGUAGES: + program_page_qset = program_page_qset.filter(language__name=language_filter) + external_program_qset = external_program_qset.filter( + language__name=language_filter + ) + course_page_qset = course_page_qset.filter(language__name=language_filter) + external_course_qset = external_course_qset.filter( + language__name=language_filter + ) + if topic_filter != ALL_TOPICS: program_page_qset = program_page_qset.related_pages(topic_filter) external_program_qset = external_program_qset.related_pages(topic_filter) - course_page_qset = course_page_qset.related_pages(topic_filter) external_course_qset = external_course_qset.related_pages(topic_filter) @@ -647,6 +663,15 @@ def get_context(self, request, *args, **kwargs): # noqa: ARG002 } for sorting_option in CatalogSorting ], + show_language_filter=is_language_filter_enabled, + selected_language=language_filter, + language_options=[ALL_LANGUAGES] + + [ + course_language.name + for course_language in CourseLanguage.objects.filter(is_active=True) + ] + if is_language_filter_enabled + else [], ) diff --git a/cms/models_test.py b/cms/models_test.py index a9bff47154..e240abff20 100644 --- a/cms/models_test.py +++ b/cms/models_test.py @@ -24,8 +24,10 @@ UPCOMING_WEBINAR, UPCOMING_WEBINAR_BUTTON_TITLE, WEBINAR_HEADER_BANNER, + ALL_LANGUAGES, ) from cms.factories import ( + CatalogPageFactory, CertificatePageFactory, CommonComponentIndexPageFactory, CompaniesLogoCarouselPageFactory, @@ -88,6 +90,7 @@ CourseRunFactory, ProgramCertificateFactory, ) +from courses.models import CourseLanguage from ecommerce.factories import ProductFactory, ProductVersionFactory pytestmark = [pytest.mark.django_db] @@ -240,6 +243,78 @@ def test_webinar_detail_page_button_title(): assert upcoming_webinar.detail_page_button_title == UPCOMING_WEBINAR_BUTTON_TITLE +@pytest.mark.parametrize( + "languages, selected_language", + [ + (None, ALL_LANGUAGES), + ( + [ + "Languag1", + "Language2", + ], + "Language2", + ), + (["Languag1", "Language2", "Language3"], "Languag1"), + ], +) +def test_catalog_page_language_context( + mocker, staff_user, languages, selected_language +): + """ + Verify the language context is properly passed to the catalog_page.html + """ + mocker.patch("cms.models.is_enabled", return_value=True) + CourseLanguage.objects.all().delete() + catalog_page = CatalogPageFactory.create() + if languages: + CourseLanguageFactory.create_batch( + len(languages), name=factory.Iterator(languages) + ) + + rf = RequestFactory() + request = rf.get(f"/?language={selected_language}") + request.user = staff_user + context = catalog_page.get_context(request=request) + + assert context.get("self") == catalog_page + assert context.get("page") == catalog_page + assert context.get("request") == request + assert context.get("selected_language") == selected_language + assert ( + context.get("language_options") == [ALL_LANGUAGES] + languages + if languages + else ALL_LANGUAGES + ) + + +@pytest.mark.parametrize( + "is_enabled", + [True, False], +) +def test_catalog_page_language_feature_flag(mocker, staff_user, is_enabled): + """ + Verify the language context is properly passed to the catalog_page.html + """ + mocker.patch("cms.models.is_enabled", return_value=is_enabled) + CourseLanguage.objects.all().delete() + catalog_page = CatalogPageFactory.create() + languages = CourseLanguageFactory.create_batch(2) + + rf = RequestFactory() + request = rf.get("/") + request.user = staff_user + context = catalog_page.get_context(request=request) + + assert context.get("selected_language") == ALL_LANGUAGES + assert context.get("show_language_filter") == is_enabled + expected_languages = ( + [ALL_LANGUAGES] + [language.name for language in languages] + if is_enabled + else [] + ) + assert context.get("language_options") == expected_languages + + def test_course_page_program_page(): """ Verify `program_page` property from the course page returns expected value diff --git a/cms/templates/catalog_page.html b/cms/templates/catalog_page.html index 1d18d0293f..e97ecdd1a6 100644 --- a/cms/templates/catalog_page.html +++ b/cms/templates/catalog_page.html @@ -21,234 +21,310 @@ <h1>{{ site_name }}—Professional Development, the MIT Way</h1> {% endblock %} <div class="catalog-content"> <div class="topics"> - {% for topic in topics %} - {% if selected_topic == topic %} - <div class="topic selected"> - {% else %} - <div class="topic"> - {% endif %} - <a href="{% pageurl page %}?topic={{ topic | urlencode }}" - >{{ topic }}</a + {% if show_language_filter %} + <div class="catalog-filter-dropdown dropdown" id="topicSortDropdown"> + <span>Topic</span> + <div + class="col-2 dropdown-toggle" + id="dropdownMenuButton" + data-toggle="dropdown" + aria-haspopup="true" + aria-expanded="false" + aria-label="topic-options" + data-display="static" + > + {{ selected_topic }} + </div> + + <div + class="dropdown-menu dropdown-menu-left" + aria-labelledby="dropdownMenuButton" + > + {% for topic in topics %} + {% if topic == selected_topic %} + <div + class="dropdown-item topic-filter-option selected" + data-filter-value="{{ topic }}" > + {% else %} + <div + class="dropdown-item topic-filter-option" + data-filter-value="{{ topic }}" + > + {% endif %} + {{ topic }} + </div> + {% endfor %} + </div> </div> - {% endfor %} - </div> - <div class="courseware"> - <div class="tab-block"> - <div class="container"> - <div class="catalog-nav-head"> - <div class="nav-tabs-container"> - <ul class="nav nav-tabs" role="tablist"> - <li class="nav-item"> - {% if active_tab == "all-tab" %} - <a - id="all-tab" - class="nav-link active" - data-toggle="tab" - href="#all" - role="tab" - aria-controls="all" - aria-selected="true" - >All</a - > - {% else %} - <a - id="all-tab" - class="nav-link" - data-toggle="tab" - href="#all" - role="tab" - aria-controls="all" - aria-selected="true" - >All</a - > - {% endif %} - </li> - <li - class="nav-item" - data-toggle="tooltip" - title="Multi-course offerings" - data-placement="bottom" - > - {% if active_tab == "programs-tab" %} - <a - id="programs-tab" - class="nav-link active" - data-toggle="tab" - href="#programs" - role="tab" - aria-controls="programs" - aria-selected="false" - >programs</a - > - {% else %} - <a - id="programs-tab" - class="nav-link" - data-toggle="tab" - href="#programs" - role="tab" - aria-controls="programs" - aria-selected="false" - >programs</a - > - {% endif %} - </li> - <li - class="nav-item" - data-toggle="tooltip" - title="Single course offerings" - data-placement="bottom" - > - {% if active_tab == "courses-tab" %} - <a - id="courses-tab" - class="nav-link active" - data-toggle="tab" - href="#courses" - role="tab" - aria-controls="courses" - aria-selected="false" - >Courses</a - > - {% else %} - <a - id="courses-tab" - class="nav-link" - data-toggle="tab" - href="#courses" - role="tab" - aria-controls="courses" - aria-selected="false" - >Courses</a - > - {% endif %} - </li> - </ul> - </div> + + <div class="catalog-filter-dropdown dropdown" id="languageSortDropdown"> + <span>Language</span> + <div + class="col-2 dropdown-toggle" + id="dropdownMenuButton" + data-toggle="dropdown" + aria-haspopup="true" + aria-expanded="false" + aria-label="language-options" + data-display="static" + > + {{ selected_language }} + </div> + + <div + class="dropdown-menu dropdown-menu-left" + aria-labelledby="dropdownMenuButton" + > + {% for language in language_options %} + {% if language == selected_language %} + <div + class="dropdown-item language-filter-option selected" + data-filter-value="{{ language }}" + > + {% else %} <div - class="catalog-sort-by-dropdown dropdown" - id="catalogSortDropdown" + class="dropdown-item language-filter-option" + data-filter-value="{{ language }}" > - <span>Sort By:</span> - <div - class="col-2 dropdown-toggle" - id="dropdownMenuButton" - data-toggle="dropdown" - aria-haspopup="true" - aria-expanded="false" - aria-label="sorting-options" - data-display="static" - > - {{ active_sorting_title }} + {% endif %} + {{ language }} + </div> + {% endfor %} + </div> + </div> + </div> + {% else %} + {% for topic in topics %} + {% if selected_topic == topic %} + <div class="topic selected"> + {% else %} + <div class="topic"> + {% endif %} + <a href="{% pageurl page %}?topic={{ topic | urlencode }}" + >{{ topic }}</a + > + </div> + {% endfor %} + </div> + {% endif %} + <div class="courseware"> + <div class="tab-block"> + <div class="container"> + <div class="catalog-nav-head"> + <div class="nav-tabs-container"> + <ul class="nav nav-tabs" role="tablist"> + <li class="nav-item"> + {% if active_tab == "all-tab" %} + <a + id="all-tab" + class="nav-link active" + data-toggle="tab" + href="#all" + role="tab" + aria-controls="all" + aria-selected="true" + >All</a + > + {% else %} + <a + id="all-tab" + class="nav-link" + data-toggle="tab" + href="#all" + role="tab" + aria-controls="all" + aria-selected="true" + >All</a + > + {% endif %} + </li> + <li + class="nav-item" + data-toggle="tooltip" + title="Multi-course offerings" + data-placement="bottom" + > + {% if active_tab == "programs-tab" %} + <a + id="programs-tab" + class="nav-link active" + data-toggle="tab" + href="#programs" + role="tab" + aria-controls="programs" + aria-selected="false" + >programs</a + > + {% else %} + <a + id="programs-tab" + class="nav-link" + data-toggle="tab" + href="#programs" + role="tab" + aria-controls="programs" + aria-selected="false" + >programs</a + > + {% endif %} + </li> + <li + class="nav-item" + data-toggle="tooltip" + title="Single course offerings" + data-placement="bottom" + > + {% if active_tab == "courses-tab" %} + <a + id="courses-tab" + class="nav-link active" + data-toggle="tab" + href="#courses" + role="tab" + aria-controls="courses" + aria-selected="false" + >Courses</a + > + {% else %} + <a + id="courses-tab" + class="nav-link" + data-toggle="tab" + href="#courses" + role="tab" + aria-controls="courses" + aria-selected="false" + >Courses</a + > + {% endif %} + </li> + </ul> </div> <div - class="dropdown-menu dropdown-menu-left" - aria-labelledby="dropdownMenuButton" + class="catalog-sort-by-dropdown dropdown" + id="catalogSortDropdown" > - {% for sorting in sort_by_options %} - {% if sorting.title == active_sorting_title %} + <span>Sort By:</span> <div - class="dropdown-item catalog-sort-option selected" - data-sort-value="{{ sorting.value }}" + class="col-2 dropdown-toggle" + id="dropdownMenuButton" + data-toggle="dropdown" + aria-haspopup="true" + aria-expanded="false" + aria-label="sorting-options" + data-display="static" > - {% else %} + {{ active_sorting_title }} + </div> + <div + class="dropdown-menu dropdown-menu-left" + aria-labelledby="dropdownMenuButton" + > + {% for sorting in sort_by_options %} + {% if sorting.title == active_sorting_title %} <div - class="dropdown-item catalog-sort-option" + class="dropdown-item catalog-sort-option selected" data-sort-value="{{ sorting.value }}" > - {% endif %} - {{ sorting.title }} + {% else %} + <div + class="dropdown-item catalog-sort-option" + data-sort-value="{{ sorting.value }}" + > + {% endif %} + {{ sorting.title }} + </div> + {% endfor %} </div> - {% endfor %} </div> </div> - </div> - <div class="tab-content"> - {% if featured_product.program %} - {% include "partials/featured_card.html" with courseware_page=featured_product object_type="program" %} - {% elif featured_product.course %} - {% include "partials/featured_card.html" with courseware_page=featured_product object_type="course"%} - {% endif %} + <div class="tab-content"> + {% if featured_product.program %} + {% include "partials/featured_card.html" with courseware_page=featured_product object_type="program" %} + {% elif featured_product.course %} + {% include "partials/featured_card.html" with courseware_page=featured_product object_type="course"%} + {% endif %} - {% if active_tab == "all-tab" %} - <div - class="tab-pane catalog-body fade in show active" - id="all" - role="tabpanel" - aria-labelledby="all-tab" - > - {% else %} + {% if active_tab == "all-tab" %} <div - class="tab-pane catalog-body fade" + class="tab-pane catalog-body fade in show active" id="all" role="tabpanel" aria-labelledby="all-tab" - > - {% endif %} - {% for page in all_pages %} - {% if page.program or page.is_external_program_page %} - {% include "partials/catalog_card.html" with courseware_page=page object_type="program" tab="all" %} - {% elif page.course or page.is_external_course_page %} - {% include "partials/catalog_card.html" with courseware_page=page object_type="course" tab="all"%} - {% endif %} - {% endfor %} - </div> - - {% if active_tab == "programs-tab" %} - <div - class="tab-pane catalog-body fade in show active" - id="programs" - role="tabpanel" - aria-labelledby="programs-tab" > {% else %} <div class="tab-pane catalog-body fade" - id="programs" + id="all" role="tabpanel" - aria-labelledby="programs-tab" + aria-labelledby="all-tab" > {% endif %} - <h1>PROGRAMS</h1> - {% for program_page in program_pages %} - {% include "partials/catalog_card.html" with courseware_page=program_page object_type="program" tab="program" %} + {% for page in all_pages %} + {% if page.program or page.is_external_program_page %} + {% include "partials/catalog_card.html" with courseware_page=page object_type="program" tab="all" %} + {% elif page.course or page.is_external_course_page %} + {% include "partials/catalog_card.html" with courseware_page=page object_type="course" tab="all"%} + {% endif %} {% endfor %} </div> - {% if active_tab == "courses-tab" %} + {% if active_tab == "programs-tab" %} <div class="tab-pane catalog-body fade in show active" - id="courses" + id="programs" role="tabpanel" - aria-labelledby="courses-tab" + aria-labelledby="programs-tab" > {% else %} <div class="tab-pane catalog-body fade" - id="courses" + id="programs" role="tabpanel" - aria-labelledby="courses-tab" + aria-labelledby="programs-tab" > {% endif %} - <h1>COURSES</h1> - {% for course_page in course_pages %} - {% include "partials/catalog_card.html" with courseware_page=course_page object_type="course" tab="course" %} + <h1>PROGRAMS</h1> + {% for program_page in program_pages %} + {% include "partials/catalog_card.html" with courseware_page=program_page object_type="program" tab="program" %} {% endfor %} </div> + + {% if active_tab == "courses-tab" %} + <div + class="tab-pane catalog-body fade in show active" + id="courses" + role="tabpanel" + aria-labelledby="courses-tab" + > + {% else %} + <div + class="tab-pane catalog-body fade" + id="courses" + role="tabpanel" + aria-labelledby="courses-tab" + > + {% endif %} + <h1>COURSES</h1> + {% for course_page in course_pages %} + {% include "partials/catalog_card.html" with courseware_page=course_page object_type="course" tab="course" %} + {% endfor %} + </div> + </div> </div> </div> </div> </div> </div> - </div> - {% endblock %} + {% endblock %} - {% block contact-us %} - {% if hubspot_new_courses_form_guid and hubspot_portal_id %} - {% include "subscription.html" %} - {% endif %} - {% endblock %} + {% block contact-us %} + {% if hubspot_new_courses_form_guid and hubspot_portal_id %} + {% include "subscription.html" %} + {% endif %} + {% endblock %} + </div> </div> </div> </div> diff --git a/cms/views_test.py b/cms/views_test.py index 50ff87b792..646bc3884c 100644 --- a/cms/views_test.py +++ b/cms/views_test.py @@ -16,6 +16,7 @@ from cms.constants import ( ALL_TOPICS, + ALL_LANGUAGES, ON_DEMAND_WEBINAR, UPCOMING_WEBINAR, WEBINAR_DEFAULT_IMAGES, @@ -48,6 +49,7 @@ TextVideoSection, ) from courses.factories import ( + CourseLanguageFactory, CourseRunCertificateFactory, CourseRunFactory, CourseTopicFactory, @@ -55,6 +57,7 @@ ProgramFactory, ProgramRunFactory, ) +from courses.models import CourseLanguage from ecommerce.factories import ProductVersionFactory from mitxpro.utils import now_in_utc @@ -441,6 +444,70 @@ def test_catalog_page_topics( # noqa: PLR0913 assert len(resp.context_data["program_pages"]) == expected_program_count +@pytest.mark.parametrize( # noqa: PT007 + "language_options, selected_language, assign_language, expected_courses_count, expected_program_count", # noqa: PT006 + [ + [["Language1", "Language2"], ALL_LANGUAGES, "Language1", 2, 2], + [["Language1", "Language2"], "Language1", "Language1", 2, 2], + [["Language1", "Language2"], "Language2", "Language2", 2, 2], + [["Language1", "Language2"], "Language2", "Language1", 0, 0], + [["Language1", "Language2"], "Language1", "Language2", 0, 0], + ], +) +def test_catalog_page_languages( # noqa: PLR0913 + mocker, + client, + wagtail_basics, + language_options, + selected_language, + assign_language, + expected_courses_count, + expected_program_count, +): + """ + Test that language filters are working fine. + """ + mocker.patch("cms.models.is_enabled", return_value=True) + CourseLanguage.objects.all().delete() + homepage = wagtail_basics.root + catalog_page = CatalogPageFactory.create(parent=homepage) + catalog_page.save_revision().publish() + + now = now_in_utc() + start_date = now + timedelta(days=2) + end_date = now + timedelta(days=10) + + courseware_languages = CourseLanguageFactory.create_batch( + 2, name=factory.Iterator(language_options) + ) + assign_language = next( + language + for language in courseware_languages + if language.name == assign_language + ) + program_pages = ProgramPageFactory.create_batch(2, language=assign_language) + + CourseRunFactory.create_batch( + 2, + course__program=factory.Iterator( + [program_page.program for program_page in program_pages] + ), + course__live=True, + course__page__language=assign_language, + start_date=start_date, + end_date=end_date, + live=True, + ) + + resp = client.get(f"{catalog_page.get_url()}?language={selected_language}") + + assert resp.status_code == status.HTTP_200_OK + assert resp.context_data["language_options"] == [ALL_LANGUAGES] + language_options + assert resp.context_data["selected_language"] == selected_language + assert len(resp.context_data["course_pages"]) == expected_courses_count + assert len(resp.context_data["program_pages"]) == expected_program_count + + def test_catalog_page_topics_ordering(client, wagtail_basics): """ Test that topics are ordered alphabetically on Catalog Page diff --git a/courses/migrations/0044_add_language_is_active.py b/courses/migrations/0044_add_language_is_active.py new file mode 100644 index 0000000000..d7cd6e0e06 --- /dev/null +++ b/courses/migrations/0044_add_language_is_active.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.18 on 2025-01-22 08:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("courses", "0043_rename_sync_daily_platform_enable_sync"), + ] + + operations = [ + migrations.AddField( + model_name="courselanguage", + name="is_active", + field=models.BooleanField(default=True), + ), + ] diff --git a/courses/models.py b/courses/models.py index e5fb3ff5b4..1a90a1408c 100644 --- a/courses/models.py +++ b/courses/models.py @@ -256,6 +256,7 @@ class CourseLanguage(TimestampedModel, ValidateOnSaveMixin): validators=[MinValueValidator(1)], help_text="The priority of this language in the course/program sorting.", ) + is_active = models.BooleanField(default=True) class Meta: constraints = [ diff --git a/mitxpro/features.py b/mitxpro/features.py index 28a77a6d5f..ebf937c790 100644 --- a/mitxpro/features.py +++ b/mitxpro/features.py @@ -3,3 +3,4 @@ DIGITAL_CREDENTIALS = "digital_credentials" ENABLE_ENTERPRISE = "enable_enterprise" ENROLLMENT_WELCOME_EMAIL = "enrollment_welcome_email" +CATALOG_LANGUAGE_FILTER = "catalog_language_filter" diff --git a/static/js/entry/django.js b/static/js/entry/django.js index 3caea352ac..2d4cd91007 100644 --- a/static/js/entry/django.js +++ b/static/js/entry/django.js @@ -21,6 +21,8 @@ import blogPostsCarousel from "../blog_posts_carousel"; import companiesLogoCarousel from "../companies_logo_carousel.js"; import successStoriesCarousel from "../success_stories_carousel.js"; import applySorting from "../catalogSorting"; +import applyTopicFilter from "../topicFilter.js"; +import applyLanguageFilter from "../languageFilter.js"; document.addEventListener("DOMContentLoaded", function () { notifications(); @@ -38,4 +40,6 @@ document.addEventListener("DOMContentLoaded", function () { companiesLogoCarousel(); successStoriesCarousel(); applySorting(); + applyTopicFilter(); + applyLanguageFilter(); }); diff --git a/static/js/languageFilter.js b/static/js/languageFilter.js new file mode 100644 index 0000000000..98fb31468d --- /dev/null +++ b/static/js/languageFilter.js @@ -0,0 +1,13 @@ +/*eslint-env jquery*/ +/*eslint semi: ["error", "always"]*/ +/* eslint-disable no-unused-vars */ +export default function applyLanguageFilter() { + $(".language-filter-option").on("click", function (event) { + event.preventDefault(); + const url = new URL(window.location.href); + const searchParams = url.searchParams; + searchParams.set("language", $(event.target).attr("data-filter-value")); + url.search = searchParams.toString(); + window.location.href = url.toString(); + }); +} diff --git a/static/js/topicFilter.js b/static/js/topicFilter.js new file mode 100644 index 0000000000..e0d7e135e2 --- /dev/null +++ b/static/js/topicFilter.js @@ -0,0 +1,13 @@ +/*eslint-env jquery*/ +/*eslint semi: ["error", "always"]*/ +/* eslint-disable no-unused-vars */ +export default function applyTopicFilter() { + $(".topic-filter-option").on("click", function (event) { + event.preventDefault(); + const url = new URL(window.location.href); + const searchParams = url.searchParams; + searchParams.set("topic", $(event.target).attr("data-filter-value")); + url.search = searchParams.toString(); + window.location.href = url.toString(); + }); +} diff --git a/static/scss/catalog/tabs.scss b/static/scss/catalog/tabs.scss index 773bb747f0..a89d4c18bb 100644 --- a/static/scss/catalog/tabs.scss +++ b/static/scss/catalog/tabs.scss @@ -201,3 +201,42 @@ } } } + +.catalog-filter-dropdown { + @extend .catalog-sort-by-dropdown; + + display: block; + margin-left: 0; + border: none; + margin-bottom: 32px; + padding: 0; + + &.show, + &:hover { + border: none; + } + + .dropdown-toggle { + border: 1px solid #dde1ec; + padding: 16px; + width: 220px; + border-radius: 4px; + text-wrap-mode: wrap; + + &::after { + float: right; + } + } + + .dropdown-menu { + .dropdown-item { + text-wrap-mode: wrap; + line-height: 25px; + margin-top: 0; + } + } + + .col-2 { + max-width: 100%; + } +}