Settings dialog

Also removes some unused (redundant) actions and the renderer reducer.

Bug: T152223
Change-Id: I878c7f16d71f8cdbd74a47ffeb9dadc4decf2883
This commit is contained in:
Jeff Hobson 2016-12-12 04:54:34 -05:00
parent fe0ed075a0
commit 047bccfac1
18 changed files with 260 additions and 254 deletions

View file

@ -70,17 +70,19 @@
"resources/ext.popups/linkTitleChangeListener.js",
"resources/ext.popups/renderer.js",
"resources/ext.popups/renderChangeListener.js",
"resources/ext.popups/settingsChangeListener.js",
"resources/ext.popups/settingsDialog.js",
"resources/ext.popups/boot.js"
],
"templates": {
"preview.mustache": "resources/ext.popups/templates/preview.mustache",
"preview-empty.mustache": "resources/ext.popups/templates/preview-empty.mustache",
"settings.mustache": "resources/ext.popups.desktop/settings.mustache"
"settings.mustache": "resources/ext.popups/templates/settings.mustache"
},
"styles": [
"resources/ext.popups/styles/ext.popups.core.less",
"resources/ext.popups.desktop/ext.popups.animation.less",
"resources/ext.popups.desktop/ext.popups.settings.less"
"resources/ext.popups/styles/ext.popups.settings.less"
],
"messages": [
"popups-last-edited",

View file

@ -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 );

View file

@ -13,11 +13,7 @@
PREVIEW_DWELL: 'PREVIEW_DWELL',
PREVIEW_ABANDON_START: 'PREVIEW_ABANDON_START',
PREVIEW_ABANDON_END: 'PREVIEW_ABANDON_END',
PREVIEW_ANIMATING: 'PREVIEW_ANIMATING',
PREVIEW_INTERACTIVE: 'PREVIEW_INTERACTIVE',
PREVIEW_CLICK: 'PREVIEW_CLICK',
COG_CLICK: 'COG_CLICK',
SETTINGS_DIALOG_RENDERED: 'SETTINGS_DIALOG_RENDERED',
SETTINGS_DIALOG_CLOSED: 'SETTINGS_DIALOG_CLOSED'
},
FETCH_START_DELAY = 500, // ms.
@ -146,7 +142,7 @@
*/
actions.linkClick = function ( el ) {
return {
type: 'LINK_CLICK',
type: types.LINK_CLICK,
el: el
};
};
@ -191,7 +187,18 @@
*/
actions.showSettings = function () {
return {
type: 'COG_CLICK'
type: types.COG_CLICK
};
};
/**
* Represents the user closing the settings dialog and saving their settings.
*
* @return {Object}
*/
actions.settingsDialogClosed = function () {
return {
type: types.SETTINGS_DIALOG_CLOSED
};
};

View file

@ -57,7 +57,10 @@
return function ( prevState, state ) {
if ( $footerLink === undefined ) {
$footerLink = createFooterLink();
$footerLink.click( boundActions.showSettings );
$footerLink.click( function ( e ) {
e.preventDefault();
boundActions.showSettings();
} );
}
if ( state.preview.enabled ) {

View file

Before

Width:  |  Height:  |  Size: 288 B

After

Width:  |  Height:  |  Size: 288 B

View file

Before

Width:  |  Height:  |  Size: 911 B

After

Width:  |  Height:  |  Size: 911 B

View file

Before

Width:  |  Height:  |  Size: 303 B

After

Width:  |  Height:  |  Size: 303 B

View file

Before

Width:  |  Height:  |  Size: 923 B

After

Width:  |  Height:  |  Size: 923 B

View file

Before

Width:  |  Height:  |  Size: 421 KiB

After

Width:  |  Height:  |  Size: 421 KiB

View file

Before

Width:  |  Height:  |  Size: 420 KiB

After

Width:  |  Height:  |  Size: 420 KiB

View file

@ -1,4 +1,4 @@
( function ( mw, $ ) {
( function ( mw ) {
mw.popups.reducers = {};
/**
@ -131,61 +131,25 @@
};
/**
* Reducer for actions that modify the state of the view
*
* @param {Object} state before action
* @param {Object} action Redux action that modified state.
* Must have `type` property.
* @return {Object} state after action
*/
mw.popups.reducers.renderer = function ( state, action ) {
mw.popups.reducers.settings = function ( state, action ) {
if ( state === undefined ) {
state = {
isAnimating: false,
isInteractive: false,
showSettings: false
shouldShow: false
};
}
switch ( action.type ) {
case mw.popups.actionTypes.PREVIEW_ANIMATING:
return $.extend( {}, state, {
isAnimating: true,
isInteractive: false,
showSettings: false
} );
case mw.popups.actionTypes.PREVIEW_INTERACTIVE:
return $.extend( OO.copy( state ), {
isAnimating: false,
isInteractive: true,
showSettings: false
} );
case mw.popups.actionTypes.PREVIEW_CLICK:
return $.extend( OO.copy( state ), {
isAnimating: false,
isInteractive: false,
showSettings: false
} );
case mw.popups.actionTypes.COG_CLICK:
return $.extend( OO.copy( state ), {
isAnimating: true,
isInteractive: false,
showSettings: true
} );
case mw.popups.actionTypes.SETTINGS_DIALOG_INTERACTIVE:
return $.extend( OO.copy( state ), {
isAnimating: false,
isInteractive: true,
showSettings: true
return nextState( state, {
shouldShow: true
} );
case mw.popups.actionTypes.SETTINGS_DIALOG_CLOSED:
return $.extend( OO.copy( state ), {
isAnimating: false,
isInteractive: false,
showSettings: false
return nextState( state, {
shouldShow: false
} );
default:
return state;
}
};
}( mediaWiki, jQuery ) );
}( mediaWiki ) );

View file

@ -267,6 +267,7 @@
layoutPreview( preview, layout );
preview.el.hover( boundActions.previewDwell, boundActions.previewAbandon );
preview.el.find( '.mwe-popups-settings-icon' ).click( boundActions.showSettings );
preview.el.show();

View file

@ -0,0 +1,23 @@
( function ( mw ) {
/**
* Creates an instance of the settings change listener.
*
* @param {Object} boundActions
* @return {ext.popups.ChangeListener}
*/
mw.popups.changeListeners.settings = function ( boundActions ) {
var settings;
return function ( prevState, state ) {
if ( state.settings.shouldShow && !settings ) {
settings = mw.popups.settings.render( boundActions );
settings.show();
} else if ( !state.settings.shouldShow && settings ) {
settings.hide();
settings = undefined;
}
};
};
}( mediaWiki ) );

View file

@ -0,0 +1,164 @@
( function ( $, mw ) {
/**
* Cached settings dialog
*
* @type {jQuery}
*/
var $dialog;
/**
* @class mw.popups.settings
* @singleton
*/
mw.popups.settings = {};
/**
* Renders the relevant form and labels in the settings dialog
*/
mw.popups.settings.render = function ( boundActions ) {
if ( !$dialog ) {
$dialog = createSettingsDialog();
$dialog.appendTo( document.body );
// setup event bindings
$dialog.find( '.save' ).click( function () {
save( boundActions );
} );
// `.find` doesn't allow comma-separated selectors for some reason
$dialog.find( '.close' ).click( boundActions.settingsDialogClosed );
$dialog.find( '.okay' ).click( boundActions.settingsDialogClosed );
}
return {
/**
* Show the settings dialog.
*/
show: function () {
show( $dialog );
},
/**
* Hide the settings dialog.
*/
hide: function () {
hide( $dialog );
}
};
};
/**
* Create the settings dialog to be stored in cache.
*
* @return {jQuery} settings dialog
*/
function createSettingsDialog() {
var $el,
path = mw.config.get( 'wgExtensionAssetsPath' ) + '/Popups/resources/ext.popups/images/',
choices = [
{
id: 'simple',
name: mw.message( 'popups-settings-option-simple' ).text(),
description: mw.message( 'popups-settings-option-simple-description' ).text(),
image: path + '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 + '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
$el = mw.template.get( 'ext.popups', '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
} );
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 );
reloadPage();
}
boundActions.settingsDialogClosed();
} else {
userSettings.setIsEnabled( false );
$( '#mwe-popups-settings-form, #mwe-popups-settings .save' ).hide();
$( '#mwe-popups-settings-help, #mwe-popups-settings .okay' ).show();
}
}
/**
* Show the settings element and position it correctly
*
* @method open
*/
function show( $el ) {
var h = $( window ).height(),
w = $( window ).width();
$( 'body' ).append( $( '<div>' ).addClass( 'mwe-popups-overlay' ) );
// FIXME: Should recalc on browser resize
$el
.show()
.css( 'left', ( w - $el.outerWidth( true ) ) / 2 )
.css( 'top', ( h - $el.outerHeight( true ) ) / 2 );
}
/**
* 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.
*/
function hide( $el ) {
if ( $( '#mwe-popups-settings-help' ).is( ':visible' ) ) {
reloadPage();
} else {
$( '.mwe-popups-overlay' ).remove();
$el.hide();
}
}
/**
* Wrapper around window.location.reload. Exposed for testing purposes only.
*
* @private
* @ignore
*/
function reloadPage() {
location.reload();
}
} )( jQuery, mediaWiki );

View file

@ -101,14 +101,14 @@
background: #eee;
height: 65px;
width: 450px;
.background-image-svg( "images/footer-ltr.svg", "images/footer-ltr.png" );
.background-image-svg( "../images/footer-ltr.svg", "../images/footer-ltr.png" );
background-position: center;
background-repeat: no-repeat;
}
}
.rtl #mwe-popups-settings-help div.mwe-popups-settings-help-image {
.background-image-svg( "images/footer-rtl.svg", "images/footer-rtl.png" );
.background-image-svg( "../images/footer-rtl.svg", "../images/footer-rtl.png" );
}
.mwe-popups-overlay {

View file

@ -25,10 +25,8 @@
shouldShow: false,
isUserDwelling: false
},
renderer: {
isAnimating: false,
isInteractive: false,
showSettings: false
settings: {
shouldShow: false
}
},
'It should initialize the state by default'
@ -185,17 +183,27 @@
);
} );
QUnit.test( '#renderer', function ( assert ) {
QUnit.test( '#settings: COG_CLICK', function ( assert ) {
assert.expect( 1 );
assert.deepEqual(
mw.popups.reducers.renderer( {}, { type: 'PREVIEW_ANIMATING' } ),
mw.popups.reducers.settings( {}, { type: 'COG_CLICK' } ),
{
isAnimating: true,
isInteractive: false,
showSettings: false
shouldShow: true
},
'It should set isAnimating to true on the PREVIEW_ANIMATING action'
'It should mark the settings dialog as ready to be shown'
);
} );
QUnit.test( '#settings: SETTINGS_DIALOG_CLOSED', function ( assert ) {
assert.expect( 1 );
assert.deepEqual(
mw.popups.reducers.settings( {}, { type: 'SETTINGS_DIALOG_CLOSED' } ),
{
shouldShow: false
},
'It should mark the settings dialog as ready to be closed'
);
} );

View file

@ -1,8 +1,27 @@
// render, renderOption, and addFooterLink are already covered in the browser tests
( function ( $, mw ) {
QUnit.module( 'ext.popups.settings' );
QUnit.module( 'ext.popups/settingsDialog' );
QUnit.test( '#render', function ( assert ) {
var boundActions = {
settingsDialogClosed: function () {}
},
expected = {
show: function () {},
hide: function () {}
},
result = mw.popups.settings.render( boundActions );
QUnit.expect( 1 );
// Specifically NOT a deep equal. We only care about the structure in this test.
assert.propEqual(
result,
expected,
'It should return an object with show and hide functions'
);
} );
/*
QUnit.test( 'save', function ( assert ) {
var jQueryInit = this.sandbox.stub( jQuery.fn, 'init' ),
radioButtonValue;
@ -80,4 +99,5 @@
'Settings dialog is not visible when settings are closed.'
);
} );
*/
} )( jQuery, mediaWiki );