From 155d1f91d1dd1924e3738618af1931fca959919f Mon Sep 17 00:00:00 2001 From: Moriel Schottlender Date: Thu, 27 Jul 2017 15:32:32 -0700 Subject: [PATCH] RCFilters: Create a sticky preference for days/limit groups - Add sticky preference for groups and the operation behind it. - Allow normalization from the UriProcessor - Backwards-compatibility for saved queries - Allow saved queries to load regardless of sticky params and to be compared correctly without the sticky params. - Add days/limit preferences and update those on change - Update the preference even if we received a new value from the URL. Bug: T171514 Bug: T171368 Change-Id: I5232f3372f0e5c981332d152faf0ab47cc470b56 --- includes/Preferences.php | 3 + .../dm/mw.rcfilters.dm.FilterGroup.js | 143 +++++++++--------- .../dm/mw.rcfilters.dm.FiltersViewModel.js | 34 +++++ .../dm/mw.rcfilters.dm.ItemModel.js | 10 -- .../dm/mw.rcfilters.dm.SavedQueriesModel.js | 10 +- .../mw.rcfilters.Controller.js | 119 +++++++++++++-- .../mw.rcfilters.UriProcessor.js | 3 +- ...w.rcfilters.ui.ChangesLimitButtonWidget.js | 3 + .../ui/mw.rcfilters.ui.DateButtonWidget.js | 3 + .../dm.FiltersViewModel.test.js | 45 +++++- 10 files changed, 277 insertions(+), 96 deletions(-) diff --git a/includes/Preferences.php b/includes/Preferences.php index 7efbef1176..de6d6813d7 100644 --- a/includes/Preferences.php +++ b/includes/Preferences.php @@ -918,6 +918,9 @@ class Preferences { $defaultPreferences['rcfilters-saved-queries'] = [ 'type' => 'api', ]; + $defaultPreferences['rcfilters-rclimit'] = [ + 'type' => 'api', + ]; if ( $config->get( 'RCWatchCategoryMembership' ) ) { $defaultPreferences['hidecategorization'] = [ diff --git a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js index 7849cc2702..5cca5d836e 100644 --- a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js +++ b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js @@ -11,6 +11,8 @@ * @cfg {string} [type='send_unselected_if_any'] Group type * @cfg {string} [view='default'] Name of the display group this group * is a part of. + * @cfg {boolean} [isSticky] This group is using a 'sticky' default; meaning + * that every time a value is changed, it becomes the new default * @cfg {string} [title] Group title * @cfg {boolean} [hidden] This group is hidden from the regular menu views * @cfg {boolean} [allowArbitrary] Allows for an arbitrary value to be added to the @@ -38,6 +40,7 @@ this.name = name; this.type = config.type || 'send_unselected_if_any'; this.view = config.view || 'default'; + this.sticky = !!config.isSticky; this.title = config.title || name; this.hidden = !!config.hidden; this.allowArbitrary = !!config.allowArbitrary; @@ -90,7 +93,6 @@ var subsetNames = [], filterItem = new mw.rcfilters.dm.FilterItem( filter.name, model, { group: model.getName(), - useDefaultAsBaseValue: !!filter.useDefaultAsBaseValue, label: filter.label || filter.name, description: filter.description || '', labelPrefixKey: model.labelPrefixKey, @@ -175,31 +177,31 @@ // For this group, the parameter is the group name, // and a single item can be selected: default or first item this.defaultParams[ this.getName() ] = defaultParam; - - // Single option means there must be a single option - // selected, so we have to either select the default - // or select the first option - this.selectItemByParamName( defaultParam ); } // Store default filter state based on default params this.defaultFilters = this.getFilterRepresentation( this.getDefaultParams() ); // Check for filters that should be initially selected by their default value - this.getItems().forEach( function ( item ) { - if ( - item.isUsingDefaultAsBaseValue() && - ( - // This setting can only be applied to these groups - // the other groups are way too complex for that - model.getType() === 'single_option' || - model.getType() === 'boolean' - ) - ) { - // Apply selection - item.toggleSelected( !!model.defaultFilters[ item.getName() ] ); - } - } ); + if ( this.isSticky() ) { + $.each( this.defaultFilters, function ( filterName, filterValue ) { + model.getItemByName( filterName ).toggleSelected( filterValue ); + } ); + } + + // Verify that single_option group has at least one item selected + if ( + this.getType() === 'single_option' && + this.getSelectedItems().length === 0 + ) { + defaultParam = groupDefault !== undefined ? + groupDefault : this.getItems()[ 0 ].getParamName(); + + // Single option means there must be a single option + // selected, so we have to either select the default + // or select the first option + this.selectItemByParamName( defaultParam ); + } }; /** @@ -235,7 +237,8 @@ // Single option means there must be a single option // selected, so we have to either select the default // or select the first option - this.currSelected = this.getItemByParamName( this.defaultParams[ this.getName() ] ); + this.currSelected = this.getItemByParamName( this.defaultParams[ this.getName() ] ) || + this.getItems()[ 0 ]; this.currSelected.toggleSelected( true ); changed = true; } @@ -245,6 +248,12 @@ this.active !== active || this.currSelected !== item ) { + if ( this.isSticky() ) { + // If this group is sticky, then change the default according to the + // current selection. + this.defaultParams = this.getParamRepresentation( this.getSelectedState() ); + } + this.active = active; this.currSelected = item; @@ -531,21 +540,11 @@ // This means we have not been given a filter representation // so we are building one based on current state filterRepresentation[ item.getName() ] = item.isSelected(); - } else if ( !filterRepresentation[ item.getName() ] ) { + } else if ( filterRepresentation[ item.getName() ] === undefined ) { // We are given a filter representation, but we have to make // sure that we fill in the missing filters if there are any - // we will assume they are all falsey, unless they have - // isUsingDefaultAsBaseValue, in which case they get their - // default state - if ( - item.isUsingDefaultAsBaseValue() && - ( - // This setting can only be applied to these groups - // the other groups are way too complex for that - model.getType() === 'single_option' || - model.getType() === 'boolean' - ) - ) { + // we will assume they are all falsey + if ( model.isSticky() ) { filterRepresentation[ item.getName() ] = !!defaultFilters[ item.getName() ]; } else { filterRepresentation[ item.getName() ] = false; @@ -609,15 +608,21 @@ * @return {Object} Filter representation */ mw.rcfilters.dm.FilterGroup.prototype.getFilterRepresentation = function ( paramRepresentation ) { - var areAnySelected, paramValues, defaultValue, item, currentValue, + var areAnySelected, paramValues, item, currentValue, oneWasSelected = false, defaultParams = this.getDefaultParams(), - defaultFilters = this.getDefaultFilters(), expandedParams = $.extend( true, {}, paramRepresentation ), model = this, paramToFilterMap = {}, result = {}; + if ( this.isSticky() ) { + // If the group is sticky, check if all parameters are represented + // and for those that aren't represented, add them with their default + // values + paramRepresentation = $.extend( true, {}, this.getDefaultParams(), paramRepresentation ); + } + paramRepresentation = paramRepresentation || {}; if ( this.getType() === 'send_unselected_if_any' || @@ -636,8 +641,7 @@ } ); $.each( expandedParams, function ( paramName, paramValue ) { - var value = paramValue, - filterItem = paramToFilterMap[ paramName ]; + var filterItem = paramToFilterMap[ paramName ]; if ( model.getType() === 'send_unselected_if_any' ) { // Flip the definition between the parameter @@ -650,13 +654,7 @@ false; } else if ( model.getType() === 'boolean' ) { // Straight-forward definition of state - if ( - filterItem.isUsingDefaultAsBaseValue() && - paramRepresentation[ filterItem.getParamName() ] === undefined - ) { - value = defaultParams[ filterItem.getParamName() ]; - } - result[ filterItem.getName() ] = !!Number( value ); + result[ filterItem.getName() ] = !!Number( paramRepresentation[ filterItem.getParamName() ] ); } } ); } else if ( this.getType() === 'string_options' ) { @@ -690,16 +688,8 @@ } else if ( this.getType() === 'single_option' ) { // There is parameter that fits a single filter and if not, get the default this.getItems().forEach( function ( filterItem ) { - var selected = false; + var selected = filterItem.getParamName() === paramRepresentation[ model.getName() ]; - if ( - filterItem.isUsingDefaultAsBaseValue() && - paramRepresentation[ model.getName() ] === undefined - ) { - selected = !!Number( paramRepresentation[ model.getName() ] ); - } else { - selected = filterItem.getParamName() === paramRepresentation[ model.getName() ]; - } result[ filterItem.getName() ] = selected; oneWasSelected = oneWasSelected || selected; } ); @@ -708,19 +698,9 @@ // Go over result and make sure all filters are represented. // If any filters are missing, they will get a falsey value this.getItems().forEach( function ( filterItem ) { - if ( - ( - // This setting can only be applied to these groups - // the other groups are way too complex for that - model.getType() === 'single_option' || - model.getType() === 'boolean' - ) && - result[ filterItem.getName() ] === undefined && - filterItem.isUsingDefaultAsBaseValue() - ) { - result[ filterItem.getName() ] = !!defaultFilters[ filterItem.getName() ]; + if ( result[ filterItem.getName() ] === undefined ) { + result[ filterItem.getName() ] = false; } - oneWasSelected = oneWasSelected || !!result[ filterItem.getName() ]; } ); // Make sure that at least one option is selected in @@ -732,14 +712,32 @@ this.getType() === 'single_option' && !oneWasSelected ) { - defaultValue = this.getDefaultParams(); - item = this.getItemByParamName( defaultValue[ this.getName() ] ); + if ( defaultParams[ this.getName() ] ) { + item = this.getItemByParamName( defaultParams[ this.getName() ] ); + } else { + item = this.getItems()[ 0 ]; + } result[ item.getName() ] = true; } return result; }; + /** + * Get current selected state of all filter items in this group + * + * @return {Object} Selected state + */ + mw.rcfilters.dm.FilterGroup.prototype.getSelectedState = function () { + var state = {}; + + this.getItems().forEach( function ( filterItem ) { + state[ filterItem.getName() ] = filterItem.isSelected(); + } ); + + return state; + }; + /** * Get item by its filter name * @@ -839,4 +837,13 @@ mw.rcfilters.dm.FilterGroup.prototype.isFullCoverage = function () { return this.fullCoverage; }; + + /** + * Check whether the group is defined as sticky default + * + * @return {boolean} Group is sticky default + */ + mw.rcfilters.dm.FilterGroup.prototype.isSticky = function () { + return this.sticky; + }; }( mediaWiki ) ); diff --git a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js index 57e618cc28..a6a22ef537 100644 --- a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js +++ b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js @@ -546,6 +546,40 @@ return result; }; + /** + * Get a parameter representation of all sticky parameters + * + * @return {Object} Sticky parameter values + */ + mw.rcfilters.dm.FiltersViewModel.prototype.getStickyParams = function () { + var result = {}; + + $.each( this.groups, function ( name, model ) { + if ( model.isSticky() ) { + $.extend( true, result, model.getDefaultParams() ); + } + } ); + + return result; + }; + + /** + * Get a filter representation of all sticky parameters + * + * @return {Object} Sticky filters values + */ + mw.rcfilters.dm.FiltersViewModel.prototype.getStickyFiltersState = function () { + var result = {}; + + $.each( this.groups, function ( name, model ) { + if ( model.isSticky() ) { + $.extend( true, result, model.getSelectedState() ); + } + } ); + + return result; + }; + /** * Analyze the groups and their filters and output an object representing * the state of the parameters they represent. diff --git a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ItemModel.js b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ItemModel.js index 54a4dbe971..aa82e218f8 100644 --- a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ItemModel.js +++ b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ItemModel.js @@ -32,7 +32,6 @@ this.namePrefix = config.namePrefix || 'item_'; this.name = this.namePrefix + param; - this.useDefaultAsBaseValue = !!config.useDefaultAsBaseValue; this.label = config.label || this.name; this.labelPrefixKey = config.labelPrefixKey; this.description = config.description || ''; @@ -249,15 +248,6 @@ return this.identifiers; }; - /** - * Check whether the item uses its default state as a base value - * - * @return {boolean} Use default as base value - */ - mw.rcfilters.dm.ItemModel.prototype.isUsingDefaultAsBaseValue = function () { - return this.useDefaultAsBaseValue; - }; - /** * Toggle the highlight feature on and off for this filter. * It only works if highlight is supported for this filter. diff --git a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.SavedQueriesModel.js b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.SavedQueriesModel.js index d6dda1e7f1..a7f3d2312f 100644 --- a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.SavedQueriesModel.js +++ b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.SavedQueriesModel.js @@ -66,13 +66,16 @@ * the above structure. * @param {Object} [baseState] An object representing the base state * so we can normalize the data + * @param {string[]} [ignoreFilters] Filters to ignore and remove from + * the data * @fires initialize */ - mw.rcfilters.dm.SavedQueriesModel.prototype.initialize = function ( savedQueries, baseState ) { + mw.rcfilters.dm.SavedQueriesModel.prototype.initialize = function ( savedQueries, baseState, ignoreFilters ) { var items = [], defaultItem = null; savedQueries = savedQueries || {}; + ignoreFilters = ignoreFilters || {}; this.baseState = baseState; @@ -91,6 +94,11 @@ // for existing users, who are only betalabs users at the moment. normalizedData.highlights.highlight = !!Number( normalizedData.highlights.highlight ); + // Backwards-compat fix: Remove sticky parameters from the 'ignoreFilters' list + ignoreFilters.forEach( function ( name ) { + delete normalizedData.filters[ name ]; + } ); + item = new mw.rcfilters.dm.SavedQueryItemModel( id, obj.label, diff --git a/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js b/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js index 8cee0a8192..73ff165a78 100644 --- a/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js +++ b/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js @@ -32,7 +32,7 @@ * @param {Object} [tagList] Tag definition */ mw.rcfilters.Controller.prototype.initialize = function ( filterStructure, namespaceStructure, tagList ) { - var parsedSavedQueries, + var parsedSavedQueries, limitDefault, controller = this, views = {}, items = [], @@ -88,6 +88,11 @@ }; } + // Convert the default from the old preference + // since the limit preference actually affects more + // than just the RecentChanges page + limitDefault = Number( mw.user.options.get( 'rcfilters-rclimit', mw.user.options.get( 'rclimit', '50' ) ) ); + // Add parameter range operations views.range = { groups: [ @@ -99,7 +104,8 @@ allowArbitrary: true, validate: $.isNumeric, sortFunc: function ( a, b ) { return Number( a.name ) - Number( b.name ); }, - 'default': mw.user.options.get( 'rclimit' ), + 'default': String( limitDefault ), + isSticky: true, filters: [ 50, 100, 250, 500 ].map( function ( num ) { return controller._createFilterDataFromNumber( num, num ); } ) @@ -117,7 +123,8 @@ ( Number( i ) * 24 ).toFixed( 2 ) : Number( i ); }, - 'default': mw.user.options.get( 'rcdays' ), + 'default': mw.user.options.get( 'rcdays', '30' ), + isSticky: true, filters: [ // Hours (1, 2, 6, 12) 0.04166, 0.0833, 0.25, 0.5, @@ -174,7 +181,9 @@ // can normalize them per each query item this.savedQueriesModel.initialize( parsedSavedQueries, - this._getBaseFilterState() + this._getBaseFilterState(), + // This is for backwards compatibility - delete all sticky filter states + Object.keys( this.filtersModel.getStickyFiltersState() ) ); // Check whether we need to load defaults. @@ -292,6 +301,7 @@ */ mw.rcfilters.Controller.prototype.resetToDefaults = function () { this.uriProcessor.updateModelBasedOnQuery( this._getDefaultParams() ); + this.updateChangesList(); }; @@ -453,7 +463,8 @@ mw.rcfilters.Controller.prototype.saveCurrentQuery = function ( label, setAsDefault ) { var queryID, highlightedItems = {}, - highlightEnabled = this.filtersModel.isHighlightEnabled(); + highlightEnabled = this.filtersModel.isHighlightEnabled(), + selectedState = this.filtersModel.getSelectedState(); // Prepare highlights this.filtersModel.getHighlightedItems().forEach( function ( item ) { @@ -463,11 +474,14 @@ // These are filter states; highlight is stored as boolean highlightedItems.highlight = this.filtersModel.isHighlightEnabled(); + // Delete all sticky filters + this._deleteStickyValuesFromFilterState( selectedState ); + // Add item queryID = this.savedQueriesModel.addNewQuery( label || mw.msg( 'rcfilters-savedqueries-defaultlabel' ), { - filters: this.filtersModel.getSelectedState(), + filters: selectedState, highlights: highlightedItems, invert: this.filtersModel.areNamespacesInverted() } @@ -535,7 +549,10 @@ highlights.highlight = highlights.highlights || highlights.highlight; // Update model state from filters - this.filtersModel.toggleFiltersSelected( data.filters ); + this.filtersModel.toggleFiltersSelected( + // Merge filters with sticky values + $.extend( true, {}, data.filters, this.filtersModel.getStickyFiltersState() ) + ); // Update namespace inverted property this.filtersModel.toggleInvertedNamespaces( !!Number( data.invert ) ); @@ -568,7 +585,8 @@ * @return {boolean} Query exists */ mw.rcfilters.Controller.prototype.findQueryMatchingCurrentState = function () { - var highlightedItems = {}; + var highlightedItems = {}, + selectedState = this.filtersModel.getSelectedState(); // Prepare highlights of the current query this.filtersModel.getItemsSupportingHighlights().forEach( function ( item ) { @@ -576,15 +594,30 @@ } ); highlightedItems.highlight = this.filtersModel.isHighlightEnabled(); + // Remove sticky filters + this._deleteStickyValuesFromFilterState( selectedState ); + return this.savedQueriesModel.findMatchingQuery( { - filters: this.filtersModel.getSelectedState(), + filters: selectedState, highlights: highlightedItems, invert: this.filtersModel.areNamespacesInverted() } ); }; + /** + * Delete sticky filters from given object + * + * @param {Object} filterState Filter state + */ + mw.rcfilters.Controller.prototype._deleteStickyValuesFromFilterState = function ( filterState ) { + // Remove sticky filters + $.each( this.filtersModel.getStickyFiltersState(), function ( filterName ) { + delete filterState[ filterName ]; + } ); + }; + /** * Get an object representing the base state of parameters * and highlights. @@ -707,6 +740,56 @@ mw.user.options.set( 'rcfilters-saved-queries', stringified ); }; + /** + * Update sticky preferences with current model state + */ + mw.rcfilters.Controller.prototype.updateStickyPreferences = function () { + // Update default sticky values with selected, whether they came from + // the initial defaults or from the URL value that is being normalized + this.updateDaysDefault( this.filtersModel.getGroup( 'days' ).getSelectedItems()[ 0 ].getParamName() ); + this.updateLimitDefault( this.filtersModel.getGroup( 'limit' ).getSelectedItems()[ 0 ].getParamName() ); + }; + + /** + * Update the limit default value + * + * @param {number} newValue New value + */ + mw.rcfilters.Controller.prototype.updateLimitDefault = function ( newValue ) { + if ( !$.isNumeric( newValue ) ) { + return; + } + + newValue = Number( newValue ); + + if ( mw.user.options.get( 'rcfilters-rclimit' ) !== newValue ) { + // Save the preference + new mw.Api().saveOption( 'rcfilters-rclimit', newValue ); + // Update the preference for this session + mw.user.options.set( 'rcfilters-rclimit', newValue ); + } + }; + + /** + * Update the days default value + * + * @param {number} newValue New value + */ + mw.rcfilters.Controller.prototype.updateDaysDefault = function ( newValue ) { + if ( !$.isNumeric( newValue ) ) { + return; + } + + newValue = Number( newValue ); + + if ( mw.user.options.get( 'rcdays' ) !== newValue ) { + // Save the preference + new mw.Api().saveOption( 'rcdays', newValue ); + // Update the preference for this session + mw.user.options.set( 'rcdays', newValue ); + } + }; + /** * Synchronize the URL with the current state of the filters * without adding an history entry. @@ -727,6 +810,10 @@ this.uriProcessor.updateModelBasedOnQuery( new mw.Uri().query ); + // Update the sticky preferences, in case we received a value + // from the URL + this.updateStickyPreferences(); + // Only update and fetch new results if it is requested if ( fetchChangesList ) { this.updateChangesList(); @@ -775,7 +862,10 @@ data = defaultSavedQueryItem.getData(); queryHighlights = data.highlights || {}; - savedParams = this.filtersModel.getParametersFromFilters( data.filters || {} ); + savedParams = this.filtersModel.getParametersFromFilters( + // Merge filters with sticky values + $.extend( true, {}, data.filters, this.filtersModel.getStickyFiltersState() ) + ); // Translate highlights to parameters savedHighlights.highlight = String( Number( queryHighlights.highlight ) ); @@ -850,11 +940,20 @@ */ mw.rcfilters.Controller.prototype._fetchChangesList = function () { var uri = this._getUpdatedUri(), + stickyParams = this.filtersModel.getStickyParams(), requestId = ++this.requestCounter, latestRequest = function () { return requestId === this.requestCounter; }.bind( this ); + // Sticky parameters override the URL params + // this is to make sure that whether we represent + // the sticky params in the URL or not (they may + // be normalized out) the sticky parameters are + // always being sent to the server with their + // current/default values + uri.extend( stickyParams ); + return $.ajax( uri.toString(), { contentType: 'html' } ) .then( // Success diff --git a/resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js b/resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js index b4ea8af24b..ba61ba9f60 100644 --- a/resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js +++ b/resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js @@ -260,7 +260,8 @@ * @return {Object} Empty parameter state */ mw.rcfilters.UriProcessor.prototype._getEmptyParameterState = function () { - return this.emptyParameterState; + // Override empty parameter state with the sticky parameter values + return $.extend( true, {}, this.emptyParameterState, this.filtersModel.getStickyParams() ); }; /** diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesLimitButtonWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesLimitButtonWidget.js index b85a89fadb..959d4ad148 100644 --- a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesLimitButtonWidget.js +++ b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesLimitButtonWidget.js @@ -87,7 +87,10 @@ * @param {string} filterName Chosen filter name */ mw.rcfilters.ui.ChangesLimitButtonWidget.prototype.onPopupLimit = function ( filterName ) { + var item = this.limitGroupModel.getItemByName( filterName ); + this.controller.toggleFilterSelect( filterName, true ); + this.controller.updateLimitDefault( item.getParamName() ); this.button.popup.toggle( false ); }; diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.DateButtonWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.DateButtonWidget.js index 647e42db6b..84dbe394ac 100644 --- a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.DateButtonWidget.js +++ b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.DateButtonWidget.js @@ -84,7 +84,10 @@ * @param {string} filterName Chosen filter name */ mw.rcfilters.ui.DateButtonWidget.prototype.onPopupDays = function ( filterName ) { + var item = this.daysGroupModel.getItemByName( filterName ); + this.controller.toggleFilterSelect( filterName, true ); + this.controller.updateDaysDefault( item.getParamName() ); this.button.popup.toggle( false ); }; diff --git a/tests/qunit/suites/resources/mediawiki.rcfilters/dm.FiltersViewModel.test.js b/tests/qunit/suites/resources/mediawiki.rcfilters/dm.FiltersViewModel.test.js index da7bafdbdb..58e4d29c42 100644 --- a/tests/qunit/suites/resources/mediawiki.rcfilters/dm.FiltersViewModel.test.js +++ b/tests/qunit/suites/resources/mediawiki.rcfilters/dm.FiltersViewModel.test.js @@ -74,10 +74,21 @@ }, { name: 'group6', type: 'boolean', + isSticky: true, filters: [ - { name: 'group6option1', label: 'group6option1-label', description: 'group5option1-desc' }, - { name: 'group6option2', label: 'group6option2-label', description: 'group5option2-desc', default: true, useDefaultAsBaseValue: true }, - { name: 'group6option3', label: 'group6option3-label', description: 'group5option3-desc', default: true } + { name: 'group6option1', label: 'group6option1-label', description: 'group6option1-desc' }, + { name: 'group6option2', label: 'group6option2-label', description: 'group6option2-desc', default: true }, + { name: 'group6option3', label: 'group6option3-label', description: 'group6option3-desc', default: true } + ] + }, { + name: 'group7', + type: 'single_option', + isSticky: true, + default: 'group7option2', + filters: [ + { name: 'group7option1', label: 'group7option1-label', description: 'group7option1-desc' }, + { name: 'group7option2', label: 'group7option2-label', description: 'group7option2-desc' }, + { name: 'group7option3', label: 'group7option3-label', description: 'group7option3-desc' } ] } ], viewsDefinition = { @@ -111,6 +122,7 @@ group6option1: '0', group6option2: '1', group6option3: '1', + group7: 'group7option2', namespace: '' }, baseParamRepresentation = { @@ -125,7 +137,8 @@ group5: 'option1', group6option1: '0', group6option2: '1', - group6option3: '0', + group6option3: '1', + group7: 'group7option2', namespace: '' }, baseFilterRepresentation = { @@ -148,7 +161,10 @@ group5__option3: false, group6__group6option1: false, group6__group6option2: true, - group6__group6option3: false, + group6__group6option3: true, + group7__group7option1: false, + group7__group7option2: true, + group7__group7option3: false, namespace__0: false, namespace__1: false, namespace__2: false, @@ -172,7 +188,10 @@ group5__option3: { selected: false, conflicted: false, included: false }, group6__group6option1: { selected: false, conflicted: false, included: false }, group6__group6option2: { selected: true, conflicted: false, included: false }, - group6__group6option3: { selected: false, conflicted: false, included: false }, + group6__group6option3: { selected: true, conflicted: false, included: false }, + group7__group7option1: { selected: false, conflicted: false, included: false }, + group7__group7option2: { selected: true, conflicted: false, included: false }, + group7__group7option3: { selected: false, conflicted: false, included: false }, namespace__0: { selected: false, conflicted: false, included: false }, namespace__1: { selected: false, conflicted: false, included: false }, namespace__2: { selected: false, conflicted: false, included: false }, @@ -246,6 +265,20 @@ defaultParameters, 'Default parameters are stored properly per filter and group' ); + + // Change sticky filter + model.toggleFiltersSelected( { + group7__group7option1: true + } ); + + // Make sure defaults have changed + assert.deepEqual( + model.getDefaultParams(), + $.extend( true, {}, defaultParameters, { + group7: 'group7option1' + } ), + 'Default parameters are stored properly per filter and group' + ); } ); QUnit.test( 'Finding matching filters', function ( assert ) { -- 2.20.1