mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Popups
synced 2024-11-15 11:46:55 +00:00
Merge "Show referencePreviews on click"
This commit is contained in:
commit
fe6c707da1
|
@ -53,7 +53,7 @@
|
|||
"bundlesize": [
|
||||
{
|
||||
"path": "resources/dist/index.js",
|
||||
"maxSize": "13.5KB"
|
||||
"maxSize": "13.6KB"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
BIN
resources/dist/index.js
vendored
BIN
resources/dist/index.js
vendored
Binary file not shown.
BIN
resources/dist/index.js.map.json
vendored
BIN
resources/dist/index.js.map.json
vendored
Binary file not shown.
|
@ -8,6 +8,7 @@ export default {
|
|||
ABANDON_START: 'ABANDON_START',
|
||||
ABANDON_END: 'ABANDON_END',
|
||||
LINK_CLICK: 'LINK_CLICK',
|
||||
REFERENCE_CLICK: 'REFERENCE_CLICK',
|
||||
/** Precedes a fetch. */
|
||||
FETCH_START: 'FETCH_START',
|
||||
/** Follows a successful fetch. */
|
||||
|
|
|
@ -308,6 +308,59 @@ export function linkClick( el ) {
|
|||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the user clicking on a reference preview link with their mouse, keyboard, or an
|
||||
* assistive device.
|
||||
*
|
||||
* @param {mw.Title} title
|
||||
* @param {Element} el
|
||||
* @param {Gateway} gateway
|
||||
* @param {Function} generateToken
|
||||
* @return {Redux.Thunk}
|
||||
*/
|
||||
export function referenceClick( title, el, gateway, generateToken ) {
|
||||
return ( dispatch, getState ) => {
|
||||
const {
|
||||
activeLink,
|
||||
activeToken: dwellToken,
|
||||
promise: dwellPromise,
|
||||
wasClicked
|
||||
} = getState().preview;
|
||||
|
||||
if ( wasClicked ) {
|
||||
return $.Deferred().resolve().promise();
|
||||
}
|
||||
|
||||
const xhr = gateway.fetchPreviewForTitle( title, el );
|
||||
|
||||
function clickFollowsDwellEvent() {
|
||||
return activeLink === el && dwellToken !== '';
|
||||
}
|
||||
|
||||
let token = dwellToken;
|
||||
if ( !clickFollowsDwellEvent() ) {
|
||||
token = generateToken();
|
||||
} else {
|
||||
dwellPromise.abort();
|
||||
}
|
||||
|
||||
dispatch( timedAction( {
|
||||
type: types.REFERENCE_CLICK,
|
||||
el,
|
||||
token
|
||||
} ) );
|
||||
|
||||
return xhr.then( ( result ) => {
|
||||
dispatch( {
|
||||
type: types.FETCH_COMPLETE,
|
||||
el,
|
||||
result,
|
||||
token
|
||||
} );
|
||||
} );
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the user dwelling on a preview with their mouse.
|
||||
*
|
||||
|
|
20
src/index.js
20
src/index.js
|
@ -263,11 +263,25 @@ function registerChangeListeners(
|
|||
boundActions.abandon();
|
||||
}
|
||||
} )
|
||||
.on( 'click', validLinkSelector, function () {
|
||||
.on( 'click', validLinkSelector, function ( event ) {
|
||||
const mwTitle = titleFromElement( this, mw.config );
|
||||
|
||||
if ( mwTitle ) {
|
||||
boundActions.linkClick( this );
|
||||
const type = getPreviewType( this, mw.config, mwTitle );
|
||||
|
||||
switch ( type ) {
|
||||
case previewTypes.TYPE_PAGE:
|
||||
boundActions.linkClick( this );
|
||||
break;
|
||||
case previewTypes.TYPE_REFERENCE:
|
||||
event.preventDefault();
|
||||
boundActions.referenceClick(
|
||||
mwTitle,
|
||||
this,
|
||||
referenceGateway,
|
||||
generateToken
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} );
|
||||
}() );
|
||||
|
|
|
@ -17,7 +17,8 @@ export default function preview( state, action ) {
|
|||
activeEvent: undefined,
|
||||
activeToken: '',
|
||||
shouldShow: false,
|
||||
isUserDwelling: false
|
||||
isUserDwelling: false,
|
||||
wasClicked: false
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -56,6 +57,14 @@ export default function preview( state, action ) {
|
|||
isUserDwelling: true
|
||||
} );
|
||||
|
||||
case actionTypes.REFERENCE_CLICK:
|
||||
return nextState( state, {
|
||||
activeLink: action.el,
|
||||
activeToken: action.token,
|
||||
isUserDwelling: true,
|
||||
wasClicked: true
|
||||
} );
|
||||
|
||||
case actionTypes.ABANDON_END:
|
||||
if ( action.token === state.activeToken && !state.isUserDwelling ) {
|
||||
return nextState( state, {
|
||||
|
@ -75,7 +84,8 @@ export default function preview( state, action ) {
|
|||
|
||||
case actionTypes.ABANDON_START:
|
||||
return nextState( state, {
|
||||
isUserDwelling: false
|
||||
isUserDwelling: false,
|
||||
wasClicked: false
|
||||
} );
|
||||
|
||||
case actionTypes.FETCH_START:
|
||||
|
|
|
@ -613,3 +613,116 @@ QUnit.test( 'PREVIEW_SEEN action not called if preview type not page', function
|
|||
);
|
||||
} );
|
||||
} );
|
||||
|
||||
QUnit.module( 'ext.popups/actions#referenceClick @integration', {
|
||||
beforeEach() {
|
||||
this.state = {
|
||||
preview: {}
|
||||
};
|
||||
this.getState = () => this.state;
|
||||
|
||||
this.gatewayDeferred = $.Deferred();
|
||||
this.gateway = {
|
||||
fetchPreviewForTitle: this.sandbox.stub().returns(
|
||||
this.gatewayDeferred.resolve( {} ).promise()
|
||||
)
|
||||
};
|
||||
|
||||
// lets just set this to always return 0
|
||||
mw.now = () => 0;
|
||||
|
||||
setupEl( this );
|
||||
}
|
||||
} );
|
||||
|
||||
QUnit.test( '#referenceClick', function ( assert ) {
|
||||
const dispatch = this.sandbox.spy();
|
||||
|
||||
this.sandbox.stub( mw, 'now' ).returns( new Date() );
|
||||
|
||||
const referenceClicked = actions.referenceClick(
|
||||
this.title, this.el, this.gateway, generateToken
|
||||
)(
|
||||
dispatch,
|
||||
this.getState
|
||||
);
|
||||
|
||||
assert.propEqual(
|
||||
dispatch.getCall( 0 ).args[ 0 ], {
|
||||
type: actionTypes.REFERENCE_CLICK,
|
||||
el: this.el,
|
||||
token: 'ABC',
|
||||
timestamp: mw.now()
|
||||
},
|
||||
'The dispatcher was called with the correct REFERENCE_CLICK arguments.'
|
||||
);
|
||||
|
||||
return referenceClicked.then( () => {
|
||||
assert.propEqual(
|
||||
dispatch.getCall( 1 ).args[ 0 ], {
|
||||
type: actionTypes.FETCH_COMPLETE,
|
||||
el: this.el,
|
||||
result: {},
|
||||
token: 'ABC'
|
||||
},
|
||||
'The dispatcher was called with the correct FETCH_COMPLETE arguments.'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
|
||||
QUnit.test( '#referenceClick doesn\'t continue when clicked several times', function ( assert ) {
|
||||
const dispatch = this.sandbox.spy();
|
||||
|
||||
this.state.preview = {
|
||||
wasClicked: true
|
||||
};
|
||||
|
||||
const referenceClicked = actions.referenceClick(
|
||||
this.title, this.el, this.gateway, generateToken
|
||||
)(
|
||||
dispatch,
|
||||
this.getState
|
||||
);
|
||||
|
||||
return referenceClicked.then( () => {
|
||||
assert.ok(
|
||||
dispatch.notCalled,
|
||||
'The dispatcher was never called.'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
|
||||
QUnit.test( '#referenceClick re-uses the linkDwell token if present', function ( assert ) {
|
||||
const dispatch = this.sandbox.spy(),
|
||||
oldDeferred = $.Deferred().promise( { abort() {} } );
|
||||
|
||||
this.state.preview = {
|
||||
activeLink: this.el,
|
||||
activeToken: 'OLD_TOKEN',
|
||||
promise: oldDeferred
|
||||
};
|
||||
|
||||
const referenceClicked = actions.referenceClick(
|
||||
this.title, this.el, this.gateway, generateToken
|
||||
)(
|
||||
dispatch,
|
||||
this.getState
|
||||
);
|
||||
|
||||
assert.propEqual(
|
||||
dispatch.getCall( 0 ).args[ 0 ], {
|
||||
type: actionTypes.REFERENCE_CLICK,
|
||||
el: this.el,
|
||||
token: 'OLD_TOKEN',
|
||||
timestamp: mw.now()
|
||||
},
|
||||
'The dispatcher was called with the correct REFERENCE_CLICK arguments.'
|
||||
);
|
||||
|
||||
return referenceClicked.then( () => {
|
||||
assert.ok(
|
||||
dispatch.calledTwice,
|
||||
'The dispatcher was called twice.'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -21,7 +21,8 @@ QUnit.test( '@@INIT', ( assert ) => {
|
|||
activeEvent: undefined,
|
||||
activeToken: '',
|
||||
shouldShow: false,
|
||||
isUserDwelling: false
|
||||
isUserDwelling: false,
|
||||
wasClicked: false
|
||||
},
|
||||
'The initial state is correct.'
|
||||
);
|
||||
|
@ -196,6 +197,7 @@ QUnit.test( 'FETCH_COMPLETE', ( assert ) => {
|
|||
{
|
||||
activeToken: token,
|
||||
isUserDwelling: false, // Set when ABANDON_START is reduced.
|
||||
wasClicked: false,
|
||||
|
||||
fetchResponse: action.result,
|
||||
shouldShow: false
|
||||
|
@ -286,8 +288,28 @@ QUnit.test( 'ABANDON_START', ( assert ) => {
|
|||
assert.deepEqual(
|
||||
preview( {}, action ),
|
||||
{
|
||||
isUserDwelling: false
|
||||
isUserDwelling: false,
|
||||
wasClicked: false
|
||||
},
|
||||
'ABANDON_START should mark the preview having been abandoned.'
|
||||
);
|
||||
} );
|
||||
|
||||
QUnit.test( 'REFERENCE_CLICK updates the state for a click', function ( assert ) {
|
||||
const action = {
|
||||
type: actionTypes.REFERENCE_CLICK,
|
||||
el: this.el,
|
||||
token: '1234567890'
|
||||
};
|
||||
|
||||
assert.deepEqual(
|
||||
preview( {}, action ),
|
||||
{
|
||||
activeLink: action.el,
|
||||
activeToken: action.token,
|
||||
isUserDwelling: true,
|
||||
wasClicked: true
|
||||
},
|
||||
'It should set active link and token as well as dwelling and click status.'
|
||||
);
|
||||
} );
|
||||
|
|
|
@ -110,8 +110,8 @@ module.exports = ( env, argv ) => ( {
|
|||
// Minified uncompressed size limits for chunks / assets and entrypoints. Keep these numbers
|
||||
// up-to-date and rounded to the nearest 10th of a kibibyte so that code sizing costs are
|
||||
// well understood. Related to bundlesize minified, gzipped compressed file size tests.
|
||||
maxAssetSize: 41 * 1024,
|
||||
maxEntrypointSize: 41 * 1024,
|
||||
maxAssetSize: 41.5 * 1024,
|
||||
maxEntrypointSize: 41.5 * 1024,
|
||||
|
||||
// The default filter excludes map files but we rename ours.
|
||||
assetFilter: ( filename ) => !filename.endsWith( srcMapExt )
|
||||
|
|
Loading…
Reference in a new issue