mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Popups
synced 2024-11-24 07:34:11 +00:00
Introduce the settings dialog UI
* Port ext.popups.desktop/ext.popups.settings.js to settingsDialog.js * Blank tests for now. Needs Qunit integration tests. * Transform into a factory function for future testing. * Saving functionality is commented out, will be removed when immplemented in the actions * Add new incomplete action saveSettings * Will perform the saveSettings async tasks and then trigger enabling or disabling popups. * Rename action settingsDialogClosed to hideSettings for consistency Change-Id: I3936d3a4bc476de16d76025139be09f1798796c4
This commit is contained in:
parent
2f75b42271
commit
4d5269320f
|
@ -75,6 +75,7 @@
|
|||
"resources/ext.popups/changeListeners/eventLogging.js",
|
||||
"resources/ext.popups/changeListeners/previewCount.js",
|
||||
"resources/ext.popups/changeListeners/settings.js",
|
||||
"resources/ext.popups/settingsDialog.js",
|
||||
"resources/ext.popups/boot.js"
|
||||
],
|
||||
"templates": {
|
||||
|
|
|
@ -1,186 +0,0 @@
|
|||
( function ( $, mw ) {
|
||||
|
||||
var currentLinkLogData,
|
||||
/**
|
||||
* @class mw.popups.settings
|
||||
* @singleton
|
||||
*/
|
||||
settings = {};
|
||||
|
||||
/**
|
||||
* The settings' dialog's section element.
|
||||
* Defined in settings.open
|
||||
* @property $element
|
||||
*/
|
||||
settings.$element = null;
|
||||
|
||||
/**
|
||||
* Renders the relevant form and labels in the settings dialog
|
||||
*
|
||||
* @method render
|
||||
*/
|
||||
settings.render = function () {
|
||||
var path = mw.config.get( 'wgExtensionAssetsPath' ) + '/Popups/resources/ext.popups.desktop/',
|
||||
choices = [
|
||||
{
|
||||
id: 'simple',
|
||||
name: mw.message( 'popups-settings-option-simple' ).text(),
|
||||
description: mw.message( 'popups-settings-option-simple-description' ).text(),
|
||||
image: path + 'images/hovercard.svg',
|
||||
isChecked: true
|
||||
},
|
||||
{
|
||||
id: 'advanced',
|
||||
name: mw.message( 'popups-settings-option-advanced' ).text(),
|
||||
description: mw.message( 'popups-settings-option-advanced-description' ).text(),
|
||||
image: path + 'images/navpop.svg'
|
||||
},
|
||||
{
|
||||
id: 'off',
|
||||
name: mw.message( 'popups-settings-option-off' ).text()
|
||||
}
|
||||
];
|
||||
|
||||
// Check if NavigationPopups is enabled
|
||||
/*global pg: false*/
|
||||
if ( typeof pg === 'undefined' || pg.fn.disablePopups === undefined ) {
|
||||
// remove the advanced option
|
||||
choices.splice( 1, 1 );
|
||||
}
|
||||
|
||||
// render the template
|
||||
settings.$element = mw.template.get( 'ext.popups.desktop', 'settings.mustache' ).render( {
|
||||
heading: mw.message( 'popups-settings-title' ).text(),
|
||||
closeLabel: mw.message( 'popups-settings-cancel' ).text(),
|
||||
saveLabel: mw.message( 'popups-settings-save' ).text(),
|
||||
helpText: mw.message( 'popups-settings-help' ).text(),
|
||||
okLabel: mw.message( 'popups-settings-help-ok' ).text(),
|
||||
descriptionText: mw.message( 'popups-settings-description' ).text(),
|
||||
choices: choices
|
||||
} );
|
||||
|
||||
// setup event bindings
|
||||
settings.$element.find( '.save' ).click( settings.save );
|
||||
settings.$element.find( '.close' ).click( settings.close );
|
||||
settings.$element.find( '.okay' ).click( function () {
|
||||
settings.close();
|
||||
settings.reloadPage();
|
||||
} );
|
||||
|
||||
$( 'body' ).append( settings.$element );
|
||||
};
|
||||
|
||||
/**
|
||||
* Save the setting to the device and close the dialog
|
||||
*
|
||||
* @method save
|
||||
*/
|
||||
settings.save = function () {
|
||||
var v = $( 'input[name=mwe-popups-setting]:checked', '#mwe-popups-settings' ).val();
|
||||
if ( v === 'simple' ) {
|
||||
mw.popups.saveEnabledState( true );
|
||||
settings.reloadPage();
|
||||
settings.close();
|
||||
} else {
|
||||
mw.popups.saveEnabledState( false );
|
||||
$( '#mwe-popups-settings-form, #mwe-popups-settings .save' ).hide();
|
||||
$( '#mwe-popups-settings-help, #mwe-popups-settings .okay' ).show();
|
||||
mw.track( 'ext.popups.event', $.extend( {}, currentLinkLogData, {
|
||||
action: 'disabled'
|
||||
} ) );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the settings element and position it correctly
|
||||
*
|
||||
* @method open
|
||||
* @param {Object} logData data to log
|
||||
*/
|
||||
settings.open = function ( logData ) {
|
||||
var
|
||||
h = $( window ).height(),
|
||||
w = $( window ).width();
|
||||
|
||||
currentLinkLogData = logData;
|
||||
|
||||
$( 'body' ).append( $( '<div>' ).addClass( 'mwe-popups-overlay' ) );
|
||||
|
||||
if ( !settings.$element ) {
|
||||
settings.render();
|
||||
}
|
||||
|
||||
// FIXME: Should recalc on browser resize
|
||||
settings.$element
|
||||
.show()
|
||||
.css( 'left', ( w - settings.$element.outerWidth( true ) ) / 2 )
|
||||
.css( 'top', ( h - settings.$element.outerHeight( true ) ) / 2 );
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the setting dialog and remove the overlay.
|
||||
* If the close button is clicked on the help dialog
|
||||
* save the setting and reload the page.
|
||||
*
|
||||
* @method close
|
||||
*/
|
||||
settings.close = function () {
|
||||
if ( $( '#mwe-popups-settings-help' ).is( ':visible' ) ) {
|
||||
settings.reloadPage();
|
||||
} else {
|
||||
$( '.mwe-popups-overlay' ).remove();
|
||||
settings.$element.hide();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a link to the footer to re-enable hovercards
|
||||
*
|
||||
* @method addFooterLink
|
||||
*/
|
||||
settings.addFooterLink = function () {
|
||||
var $setting, $footer;
|
||||
|
||||
if ( mw.popups.enabled ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$setting = $( '<li>' ).append(
|
||||
$( '<a>' )
|
||||
.attr( 'href', '#' )
|
||||
.text( mw.message( 'popups-settings-enable' ).text() )
|
||||
.click( function ( e ) {
|
||||
settings.open();
|
||||
e.preventDefault();
|
||||
} )
|
||||
);
|
||||
$footer = $( '#footer-places, #f-list' );
|
||||
|
||||
// From https://en.wikipedia.org/wiki/MediaWiki:Gadget-ReferenceTooltips.js
|
||||
if ( $footer.length === 0 ) {
|
||||
$footer = $( '#footer li' ).parent();
|
||||
}
|
||||
$footer.append( $setting );
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper around window.location.reload. Exposed for testing purposes only.
|
||||
*
|
||||
* @private
|
||||
* @ignore
|
||||
*/
|
||||
settings.reloadPage = function () {
|
||||
location.reload();
|
||||
};
|
||||
|
||||
$( function () {
|
||||
if ( !mw.popups.enabled ) {
|
||||
settings.addFooterLink();
|
||||
}
|
||||
} );
|
||||
|
||||
mw.popups.settings = settings;
|
||||
|
||||
} )( jQuery, mediaWiki );
|
|
@ -255,12 +255,24 @@
|
|||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
actions.settingsDialogClosed = function () {
|
||||
actions.hideSettings = function () {
|
||||
return {
|
||||
type: types.SETTINGS_HIDE
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the user saving their settings.
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
actions.saveSettings = function () {
|
||||
return function ( dispatch ) {
|
||||
// ...
|
||||
return dispatch( actions.hideSettings() );
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the queued event being logged
|
||||
* `mw.popups.changeListeners.eventLogging` change listener.
|
||||
|
|
|
@ -31,8 +31,9 @@
|
|||
* @param {Object} actions
|
||||
* @param {mw.eventLog.Schema} schema
|
||||
* @param {ext.popups.UserSettings} userSettings
|
||||
* @param {Function} settingsDialog
|
||||
*/
|
||||
function registerChangeListeners( store, actions, schema, userSettings ) {
|
||||
function registerChangeListeners( store, actions, schema, userSettings, settingsDialog ) {
|
||||
|
||||
// Sugar.
|
||||
var changeListeners = mw.popups.changeListeners,
|
||||
|
@ -43,6 +44,7 @@
|
|||
registerChangeListener( store, changeListeners.render( actions ) );
|
||||
registerChangeListener( store, changeListeners.eventLogging( actions, schema ) );
|
||||
registerChangeListener( store, changeListeners.previewCount( userSettings ) );
|
||||
registerChangeListener( store, changeListeners.settings( actions, settingsDialog ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -85,10 +87,12 @@
|
|||
generateToken = mw.user.generateRandomSessionId,
|
||||
gateway = createGateway(),
|
||||
userSettings,
|
||||
settingsDialog,
|
||||
isUserInCondition,
|
||||
schema;
|
||||
|
||||
userSettings = mw.popups.createUserSettings( mw.storage, mw.user );
|
||||
settingsDialog = mw.popups.createSettingsDialogRenderer();
|
||||
isUserInCondition = mw.popups.createExperiment( mw.config, mw.user, userSettings );
|
||||
schema = mw.popups.createSchema( mw.config, window );
|
||||
|
||||
|
@ -104,7 +108,7 @@
|
|||
) )
|
||||
);
|
||||
actions = createBoundActions( store );
|
||||
registerChangeListeners( store, actions, schema, userSettings );
|
||||
registerChangeListeners( store, actions, schema, userSettings, settingsDialog );
|
||||
|
||||
actions.boot(
|
||||
isUserInCondition,
|
||||
|
|
|
@ -8,15 +8,22 @@
|
|||
* @return {ext.popups.ChangeListener}
|
||||
*/
|
||||
mw.popups.changeListeners.settings = function ( boundActions, render ) {
|
||||
var settings;
|
||||
var settings,
|
||||
shown = false;
|
||||
|
||||
return function ( prevState, state ) {
|
||||
if ( state.settings.shouldShow && !settings ) {
|
||||
settings = render( boundActions );
|
||||
if ( state.settings.shouldShow && !shown ) {
|
||||
// Lazily instantiate the settings UI
|
||||
if ( !settings ) {
|
||||
settings = render( boundActions );
|
||||
settings.appendTo( document.body );
|
||||
}
|
||||
|
||||
settings.show();
|
||||
shown = true;
|
||||
} else if ( !state.settings.shouldShow && settings ) {
|
||||
settings.hide();
|
||||
settings = undefined;
|
||||
shown = false;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
153
resources/ext.popups/settingsDialog.js
Normal file
153
resources/ext.popups/settingsDialog.js
Normal file
|
@ -0,0 +1,153 @@
|
|||
( function ( mw, $ ) {
|
||||
|
||||
/**
|
||||
* Creates a render function that will create the settings dialog and return
|
||||
* a set of methods to operate on it
|
||||
*/
|
||||
mw.popups.createSettingsDialogRenderer = function () {
|
||||
|
||||
/**
|
||||
* Cached settings dialog
|
||||
*
|
||||
* @type {jQuery}
|
||||
*/
|
||||
var $dialog,
|
||||
/**
|
||||
* Cached settings overlay
|
||||
*
|
||||
* @type {jQuery}
|
||||
*/
|
||||
$overlay;
|
||||
|
||||
/**
|
||||
* Renders the relevant form and labels in the settings dialog
|
||||
*/
|
||||
return function ( boundActions ) {
|
||||
|
||||
if ( !$dialog ) {
|
||||
$dialog = createSettingsDialog();
|
||||
$overlay = $( '<div>' ).addClass( 'mwe-popups-overlay' );
|
||||
|
||||
// Setup event bindings
|
||||
$dialog.find( '.save' ).click( boundActions.saveSettings );
|
||||
$dialog.find( '.close, .okay' ).click( boundActions.hideSettings );
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* Append the dialog and overlay to a DOM element
|
||||
* @param {HTMLElement} el
|
||||
*/
|
||||
appendTo: function ( el ) {
|
||||
$overlay.appendTo( el );
|
||||
$dialog.appendTo( el );
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the settings element and position it correctly
|
||||
*/
|
||||
show: function () {
|
||||
var h = $( window ).height(),
|
||||
w = $( window ).width();
|
||||
|
||||
$overlay.show();
|
||||
|
||||
// FIXME: Should recalc on browser resize
|
||||
$dialog
|
||||
.show()
|
||||
.css( 'left', ( w - $dialog.outerWidth( true ) ) / 2 )
|
||||
.css( 'top', ( h - $dialog.outerHeight( true ) ) / 2 );
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the settings dialog.
|
||||
*/
|
||||
hide: function () {
|
||||
if ( $dialog.find( '#mwe-popups-settings-help' ).is( ':visible' ) ) {
|
||||
// TODO: Why is this trying to reload the page?
|
||||
// reloadPage();
|
||||
return;
|
||||
} else {
|
||||
$overlay.hide();
|
||||
$dialog.hide();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the settings dialog
|
||||
*
|
||||
* @return {jQuery} settings dialog
|
||||
*/
|
||||
function createSettingsDialog() {
|
||||
var $el,
|
||||
path = mw.config.get( 'wgExtensionAssetsPath' ) + '/Popups/resources/ext.popups/images/',
|
||||
choices = [
|
||||
{
|
||||
id: 'simple',
|
||||
name: mw.msg( 'popups-settings-option-simple' ),
|
||||
description: mw.msg( 'popups-settings-option-simple-description' ),
|
||||
image: path + 'hovercard.svg',
|
||||
isChecked: true
|
||||
},
|
||||
{
|
||||
id: 'advanced',
|
||||
name: mw.msg( 'popups-settings-option-advanced' ),
|
||||
description: mw.msg( 'popups-settings-option-advanced-description' ),
|
||||
image: path + 'navpop.svg'
|
||||
},
|
||||
{
|
||||
id: 'off',
|
||||
name: mw.msg( 'popups-settings-option-off' )
|
||||
}
|
||||
];
|
||||
|
||||
// Check if NavigationPopups is enabled
|
||||
/*global pg: false*/
|
||||
if ( typeof pg === 'undefined' || pg.fn.disablePopups === undefined ) {
|
||||
// remove the advanced option
|
||||
choices.splice( 1, 1 );
|
||||
}
|
||||
|
||||
// render the template
|
||||
$el = mw.template.get( 'ext.popups', 'settings.mustache' ).render( {
|
||||
heading: mw.msg( 'popups-settings-title' ),
|
||||
closeLabel: mw.msg( 'popups-settings-cancel' ),
|
||||
saveLabel: mw.msg( 'popups-settings-save' ),
|
||||
helpText: mw.msg( 'popups-settings-help' ),
|
||||
okLabel: mw.msg( 'popups-settings-help-ok' ),
|
||||
descriptionText: mw.msg( 'popups-settings-description' ),
|
||||
choices: choices
|
||||
} );
|
||||
|
||||
return $el;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the setting to the device and close the dialog
|
||||
*
|
||||
* @method save
|
||||
*
|
||||
function save( boundActions ) {
|
||||
var v = $( 'input[name=mwe-popups-setting]:checked', '#mwe-popups-settings' ).val(),
|
||||
userSettings = mw.popups.createUserSettings( mw.storage, mw.user );
|
||||
|
||||
if ( v === 'simple' ) {
|
||||
// Avoid a refresh if 'enabled' -> 'enabled'
|
||||
if ( !userSettings.getIsEnabled() ) {
|
||||
userSettings.setIsEnabled( true );
|
||||
// TODO: Why is this trying to reload the page?
|
||||
// reloadPage();
|
||||
}
|
||||
boundActions.hideSettings();
|
||||
} else {
|
||||
userSettings.setIsEnabled( false );
|
||||
$( '#mwe-popups-settings-form, #mwe-popups-settings .save' ).hide();
|
||||
$( '#mwe-popups-settings-help, #mwe-popups-settings .okay' ).show();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
} )( mediaWiki, jQuery );
|
|
@ -1,83 +0,0 @@
|
|||
// render, renderOption, and addFooterLink are already covered in the browser tests
|
||||
|
||||
( function ( $, mw ) {
|
||||
QUnit.module( 'ext.popups.settings' );
|
||||
|
||||
QUnit.test( 'save', function ( assert ) {
|
||||
var jQueryInit = this.sandbox.stub( jQuery.fn, 'init' ),
|
||||
radioButtonValue;
|
||||
|
||||
QUnit.expect( 2 );
|
||||
|
||||
this.sandbox.stub( mw.popups.settings, 'reloadPage' );
|
||||
this.sandbox.stub( mw.popups.settings, 'close' );
|
||||
jQueryInit.withArgs( 'input[name=mwe-popups-setting]:checked', '#mwe-popups-settings' )
|
||||
.returns( {
|
||||
val: function () {
|
||||
return radioButtonValue;
|
||||
}
|
||||
} );
|
||||
jQueryInit.withArgs( '#mwe-popups-settings-form, #mwe-popups-settings .save' )
|
||||
.returns( {
|
||||
hide: $.noop
|
||||
} );
|
||||
jQueryInit.withArgs( '#mwe-popups-settings-help, #mwe-popups-settings .okay' )
|
||||
.returns( {
|
||||
show: $.noop
|
||||
} );
|
||||
|
||||
radioButtonValue = 'simple';
|
||||
mw.popups.settings.save();
|
||||
assert.equal(
|
||||
mw.storage.get( 'mwe-popups-enabled' ),
|
||||
'1',
|
||||
'Popups are enabled when the `simple` radio button is checked.'
|
||||
);
|
||||
|
||||
radioButtonValue = 'off';
|
||||
mw.popups.settings.save();
|
||||
assert.equal(
|
||||
mw.storage.get( 'mwe-popups-enabled' ),
|
||||
'0',
|
||||
'Popups are disabled when the `off` radio button is checked.'
|
||||
);
|
||||
|
||||
jQueryInit.restore();
|
||||
mw.popups.settings.reloadPage.restore();
|
||||
mw.popups.settings.close.restore();
|
||||
} );
|
||||
|
||||
QUnit.test( 'open', function ( assert ) {
|
||||
QUnit.expect( 2 );
|
||||
|
||||
mw.popups.settings.open();
|
||||
assert.equal(
|
||||
( $( window ).width() - mw.popups.settings.$element.outerWidth( true ) ) / 2 + 'px',
|
||||
mw.popups.settings.$element.css( 'left' ),
|
||||
'Settings dialog is horizontally aligned in the middle.'
|
||||
);
|
||||
assert.equal(
|
||||
( $( window ).height() - mw.popups.settings.$element.outerHeight( true ) ) / 2 + 'px',
|
||||
mw.popups.settings.$element.css( 'top' ),
|
||||
'Settings dialog is vertically aligned in the middle.'
|
||||
);
|
||||
mw.popups.settings.close();
|
||||
} );
|
||||
|
||||
QUnit.test( 'close', function ( assert ) {
|
||||
QUnit.expect( 2 );
|
||||
|
||||
mw.popups.settings.open();
|
||||
assert.equal(
|
||||
mw.popups.settings.$element.is( ':visible' ),
|
||||
true,
|
||||
'Settings dialog is visible when settings are opened.'
|
||||
);
|
||||
mw.popups.settings.close();
|
||||
assert.equal(
|
||||
mw.popups.settings.$element.is( ':visible' ),
|
||||
false,
|
||||
'Settings dialog is not visible when settings are closed.'
|
||||
);
|
||||
} );
|
||||
} )( jQuery, mediaWiki );
|
|
@ -4,6 +4,7 @@
|
|||
setup: function () {
|
||||
this.render = this.sandbox.stub();
|
||||
this.rendered = {
|
||||
appendTo: this.sandbox.spy(),
|
||||
show: this.sandbox.spy(),
|
||||
hide: this.sandbox.spy()
|
||||
};
|
||||
|
@ -33,6 +34,7 @@
|
|||
this.settings( this.defaultState, this.showState );
|
||||
|
||||
assert.ok( this.render.calledWith( 'actions' ), 'The renderer should be called with the actions' );
|
||||
assert.ok( this.rendered.appendTo.called, 'The rendered object should be in the DOM' );
|
||||
assert.ok( this.rendered.show.called, 'The rendered object should be showed' );
|
||||
} );
|
||||
|
||||
|
@ -42,6 +44,7 @@
|
|||
this.settings( this.showState, this.showState );
|
||||
|
||||
assert.ok( this.render.calledOnce, 'The renderer should be called only the first time' );
|
||||
assert.ok( this.rendered.appendTo.calledOnce, 'The rendered object should be in the DOM' );
|
||||
assert.ok( this.rendered.show.calledOnce, 'The rendered object should be showed just once' );
|
||||
assert.notOk( this.rendered.hide.called, 'The rendered object should not be hidden' );
|
||||
} );
|
||||
|
|
26
tests/qunit/ext.popups/settingsDialog.test.js
Normal file
26
tests/qunit/ext.popups/settingsDialog.test.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
( function ( $, mw ) {
|
||||
QUnit.module( 'ext.popups/settingsDialog' );
|
||||
|
||||
QUnit.test( '#render', function ( assert ) {
|
||||
var boundActions = {
|
||||
saveSettings: function () {},
|
||||
hideSettings: function () {}
|
||||
},
|
||||
expected = {
|
||||
appendTo: function () {},
|
||||
show: function () {},
|
||||
hide: function () {}
|
||||
},
|
||||
result = mw.popups.createSettingsDialogRenderer()( boundActions );
|
||||
|
||||
// Specifically NOT a deep equal. Only structure.
|
||||
assert.propEqual(
|
||||
result,
|
||||
expected
|
||||
);
|
||||
} );
|
||||
|
||||
// FIXME: Add Qunit integration tests about the rendering and the behavior of
|
||||
// the settings dialog.
|
||||
|
||||
} )( jQuery, mediaWiki );
|
Loading…
Reference in a new issue