diff --git a/lms/static/js/discovery/discovery_factory.js b/lms/static/js/discovery/discovery_factory.js index 0e44d8154a84..467fa4940318 100644 --- a/lms/static/js/discovery/discovery_factory.js +++ b/lms/static/js/discovery/discovery_factory.js @@ -21,7 +21,23 @@ userLanguage: userLanguage, userTimezone: userTimezone }; - if (setDefaultFilter && userLanguage) { + // Read facet filters from URL query parameters and apply them. + var urlParams = new URLSearchParams(window.location.search); + urlParams.forEach(function(value, key) { + if (key === 'search_query') { + return; // handled separately via the searchQuery argument + } + if (key in meanings) { + filters.add({ + type: key, + query: value, + name: refineSidebar.termName(key, value) + }); + } + }); + + // Apply the default language filter, except when provided via URL parameters. + if (setDefaultFilter && userLanguage && !filters.get('language')) { filters.add({ type: 'language', query: userLanguage, @@ -30,6 +46,20 @@ } listing = new CoursesListing({model: courseListingModel}); + function updateUrl() { + var params = new URLSearchParams(); + filters.each(function(filter) { + if (filter.id === 'search_query') { + params.set('search_query', filter.get('query')); + } else { + params.set(filter.id, filter.get('query')); + } + }); + var qs = params.toString(); + var newUrl = window.location.pathname + (qs ? '?' + qs : ''); + history.replaceState(null, '', newUrl); + } + dispatcher.listenTo(form, "search", function (query) { form.showLoadingIndicator(); if (!query || query.trim() === "") { @@ -79,6 +109,7 @@ form.hideLoadingIndicator(); listing.render(); refineSidebar.render(); + updateUrl(); }); dispatcher.listenTo(search, 'error', function() { diff --git a/lms/static/js/spec/discovery/discovery_factory_spec.js b/lms/static/js/spec/discovery/discovery_factory_spec.js index 6ce1a9e2d657..beabc9717f80 100644 --- a/lms/static/js/spec/discovery/discovery_factory_spec.js +++ b/lms/static/js/spec/discovery/discovery_factory_spec.js @@ -225,4 +225,100 @@ define([ expect(request.requestBody).toMatch(/language=en/); }); }); + + describe('discovery.DiscoveryFactory URL sync', function() { + var originalSearch; + var replaceStateSpy; + + function setUrlSearch(search) { + // Override window.location.search for the factory to read + originalSearch = window.location.search; + // Use history.replaceState to set query parameters without navigation + history.replaceState(null, '', window.location.pathname + search); + } + + beforeEach(function() { + originalSearch = window.location.search; + replaceStateSpy = spyOn(history, 'replaceState').and.callThrough(); + loadFixtures('js/fixtures/discovery.html'); + TemplateHelpers.installTemplates([ + 'templates/discovery/course_card', + 'templates/discovery/facet', + 'templates/discovery/facet_option', + 'templates/discovery/filter', + 'templates/discovery/filter_bar' + ]); + jasmine.clock().install(); + }); + + afterEach(function() { + jasmine.clock().uninstall(); + // Restore original URL + history.replaceState(null, '', window.location.pathname + originalSearch); + }); + + it('pre-populates filters from URL query params', function() { + setUrlSearch('?modes=honor'); + DiscoveryFactory(MEANINGS); + + var requests = AjaxHelpers.requests(this); + $('.discovery-submit').trigger('click'); + var request = AjaxHelpers.currentRequest(requests); + expect(request.requestBody).toMatch(/modes=honor/); + expect($('.active-filter').length).toBe(1); + }); + + it('overrides default language filter with URL param', function() { + // Set URL with language filter different from the default + setUrlSearch('?language=hr'); + // Call DiscoveryFactory with userLanguage='en' and setDefaultFilter=true + DiscoveryFactory(MEANINGS, '', 'en', 'Europe/Lisbon', true); + + var requests = AjaxHelpers.requests(this); + $('.discovery-submit').trigger('click'); + var request = AjaxHelpers.currentRequest(requests); + expect(request.requestBody).toMatch(/language=hr/); + expect(request.requestBody).not.toMatch(/language=en/); + }); + + it('ignores unrecognized URL params', function() { + setUrlSearch('?bogus=value'); + DiscoveryFactory(MEANINGS); + + var requests = AjaxHelpers.requests(this); + $('.discovery-submit').trigger('click'); + var request = AjaxHelpers.currentRequest(requests); + expect(request.requestBody).not.toMatch(/bogus/); + expect($('.active-filter').length).toBe(0); + }); + + it('updates URL after search completes', function() { + setUrlSearch(''); + DiscoveryFactory(MEANINGS); + + var requests = AjaxHelpers.requests(this); + $('.discovery-input').val('test'); + $('.discovery-submit').trigger('click'); + AjaxHelpers.respondWithJson(requests, JSON_RESPONSE); + expect(replaceStateSpy).toHaveBeenCalled(); + var lastCall = replaceStateSpy.calls.mostRecent(); + expect(lastCall.args[2]).toMatch(/search_query=test/); + }); + + it('clears URL params when filters are reset', function() { + setUrlSearch('?language=en'); + DiscoveryFactory(MEANINGS); + + var requests = AjaxHelpers.requests(this); + // Trigger initial search to load results + $('.discovery-submit').trigger('click'); + AjaxHelpers.respondWithJson(requests, JSON_RESPONSE); + // Clear all filters + $('#clear-all-filters').trigger('click'); + // The clear triggers a new search with empty query + AjaxHelpers.respondWithJson(requests, JSON_RESPONSE); + var lastCall = replaceStateSpy.calls.mostRecent(); + expect(lastCall.args[2]).toBe(window.location.pathname); + }); + }); });