mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Popups
synced 2024-11-23 23:24:39 +00:00
Add reducers
Reducers as a whole are a WIP, but this implements a baseline from which to add more. Changes: * Create ext.popups.reducers to house all reducers * Create reducers for preview and renderer state manipulation * Create rootReducer by combining preview and renderer reducers * Add QUnit tests for reducers * Move action types into ext.popups.actionTypes * Extract rootReducer from boot.js Change-Id: I8a2296c6846cd4b0552a485e671af1d974944195
This commit is contained in:
parent
722bfe12a5
commit
2215560866
|
@ -63,6 +63,7 @@
|
|||
"resources/ext.popups/actions.js",
|
||||
"resources/ext.popups/processLinks.js",
|
||||
"resources/ext.popups/gateway.js",
|
||||
"resources/ext.popups/reducers.js",
|
||||
"resources/ext.popups/boot.js"
|
||||
],
|
||||
"templates": {
|
||||
|
|
|
@ -1,13 +1,28 @@
|
|||
( function ( mw, Redux ) {
|
||||
|
||||
var actions = {};
|
||||
var actions = {},
|
||||
types = {
|
||||
BOOT: 'BOOT',
|
||||
LINK_DWELL: 'LINK_DWELL',
|
||||
LINK_ABANDON: 'LINK_ABANDON',
|
||||
LINK_CLICK: 'LINK_CLICK',
|
||||
FETCH_START: 'FETCH_START',
|
||||
FETCH_END: 'FETCH_END',
|
||||
FETCH_FAILED: 'FETCH_FAILED',
|
||||
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'
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Function} isUserInCondition See `mw.popups.createExperiment`
|
||||
*/
|
||||
actions.boot = function ( isUserInCondition ) {
|
||||
return {
|
||||
type: 'BOOT',
|
||||
type: types.BOOT,
|
||||
isUserInCondition: isUserInCondition()
|
||||
};
|
||||
};
|
||||
|
@ -21,7 +36,7 @@
|
|||
*/
|
||||
actions.linkDwell = function ( $el ) {
|
||||
return {
|
||||
type: 'LINK_DWELL',
|
||||
type: types.LINK_DWELL,
|
||||
el: $el
|
||||
};
|
||||
};
|
||||
|
@ -36,11 +51,13 @@
|
|||
*/
|
||||
actions.linkAbandon = function ( $el ) {
|
||||
return {
|
||||
type: 'LINK_ABANDON',
|
||||
type: types.LINK_ABANDON,
|
||||
el: $el
|
||||
};
|
||||
};
|
||||
|
||||
mw.popups.actionTypes = types;
|
||||
|
||||
/**
|
||||
* Represents the user clicking on a link with their mouse, keyboard, or an
|
||||
* assistive device.
|
||||
|
|
|
@ -9,18 +9,6 @@
|
|||
'.cancelLink a'
|
||||
];
|
||||
|
||||
/**
|
||||
* A [null](https://en.wikipedia.org/wiki/Null_Object_pattern) reducer.
|
||||
*
|
||||
* @param {Object} state The current state
|
||||
* @param {Object} action The action that was dispatched against the store
|
||||
* @return {Object} The new state
|
||||
*/
|
||||
function rootReducer( state, action ) {
|
||||
/* jshint unused: false */
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the user is in the experiment group
|
||||
*
|
||||
|
@ -47,7 +35,7 @@
|
|||
}
|
||||
|
||||
store = Redux.createStore(
|
||||
rootReducer,
|
||||
mw.popups.reducers.rootReducer,
|
||||
compose( Redux.applyMiddleware(
|
||||
ReduxThunk.default
|
||||
) )
|
||||
|
|
71
resources/ext.popups/reducers.js
Normal file
71
resources/ext.popups/reducers.js
Normal file
|
@ -0,0 +1,71 @@
|
|||
( function ( mw, $, Redux ) {
|
||||
mw.popups.reducers = {};
|
||||
|
||||
/**
|
||||
* Reducer for actions that modify the state of the preview model
|
||||
*
|
||||
* @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.preview = function ( state, action ) {
|
||||
if ( state === undefined ) {
|
||||
state = {
|
||||
enabled: false,
|
||||
activeLink: undefined,
|
||||
previousActiveLink: undefined,
|
||||
interactionStarted: undefined,
|
||||
isDelayingFetch: false,
|
||||
isFetching: false,
|
||||
linkInteractionToken: undefined
|
||||
};
|
||||
}
|
||||
|
||||
switch ( action.type ) {
|
||||
case mw.popups.actionTypes.BOOT:
|
||||
return $.extend( {}, state, {
|
||||
enabled: action.isUserInCondition
|
||||
} );
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 ) {
|
||||
if ( state === undefined ) {
|
||||
state = {
|
||||
isAanimating: false,
|
||||
isInteractive: false,
|
||||
showSettings: false
|
||||
};
|
||||
}
|
||||
|
||||
switch ( action.type ) {
|
||||
case mw.popups.actionTypes.PREVIEW_ANIMATING:
|
||||
return $.extend( {}, state, {
|
||||
isAnimating: true
|
||||
} );
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Root reducer for all actions
|
||||
*
|
||||
* @param {Object} global state before action
|
||||
* @param {Object} action Redux action that modified state.
|
||||
* Must have `type` property.
|
||||
* @return {Object} global state after action
|
||||
*/
|
||||
mw.popups.reducers.rootReducer = Redux.combineReducers( mw.popups.reducers );
|
||||
}( mediaWiki, jQuery, Redux ) );
|
60
tests/qunit/ext.popups/reducers.test.js
Normal file
60
tests/qunit/ext.popups/reducers.test.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
( function ( mw ) {
|
||||
|
||||
QUnit.module( 'ext.popups/reducers' );
|
||||
|
||||
QUnit.test( '#rootReducer', function ( assert ) {
|
||||
var state = mw.popups.reducers.rootReducer( undefined, { type: '@@INIT' } );
|
||||
|
||||
assert.expect( 1 );
|
||||
|
||||
assert.deepEqual(
|
||||
state,
|
||||
{
|
||||
preview: {
|
||||
enabled: false,
|
||||
activeLink: undefined,
|
||||
previousActiveLink: undefined,
|
||||
interactionStarted: undefined,
|
||||
isDelayingFetch: false,
|
||||
isFetching: false,
|
||||
linkInteractionToken: undefined
|
||||
},
|
||||
renderer: {
|
||||
isAanimating: false,
|
||||
isInteractive: false,
|
||||
showSettings: false
|
||||
}
|
||||
},
|
||||
'It should initialize the state by default'
|
||||
);
|
||||
} );
|
||||
|
||||
QUnit.test( '#model', function ( assert ) {
|
||||
var state = mw.popups.reducers.preview(
|
||||
{},
|
||||
{
|
||||
type: 'BOOT',
|
||||
isUserInCondition: true
|
||||
}
|
||||
);
|
||||
|
||||
assert.expect( 1 );
|
||||
|
||||
assert.ok(
|
||||
state.enabled,
|
||||
'It should set enabled to true when the user is in the enabled condition.'
|
||||
);
|
||||
} );
|
||||
|
||||
QUnit.test( '#renderer', function ( assert ) {
|
||||
assert.expect( 1 );
|
||||
|
||||
assert.deepEqual(
|
||||
// FIXME: There may be more to the action object when this action is implemented
|
||||
mw.popups.reducers.renderer( {}, { type: 'PREVIEW_ANIMATING' } ),
|
||||
{ isAnimating: true },
|
||||
'It should set isAnimating to true when the preview begins rendering.'
|
||||
);
|
||||
} );
|
||||
}( mediaWiki ) );
|
||||
|
Loading…
Reference in a new issue