Use OOjs Popup Widgets instead of tipsy

Bug: T141983
Change-Id: I4b4ea427f82b15625537d859623461eceb1eb52b
This commit is contained in:
Leszek Manicki 2016-08-03 14:08:05 +02:00
parent 0af4fe35d5
commit 77095bfa65
7 changed files with 142 additions and 94 deletions

View file

@ -109,7 +109,6 @@ class RevisionSliderHooks {
'ext.RevisionSlider.HelpDialog',
'jquery.ui.draggable',
'jquery.ui.tooltip',
'jquery.tipsy',
'oojs-ui'
],
'localBasePath' => __DIR__,

View file

@ -133,7 +133,7 @@
"revisionslider-minoredit"
],
"dependencies": [
"jquery.tipsy"
"oojs-ui"
]
},
"ext.RevisionSlider.arrows.left": {

View file

@ -13,15 +13,25 @@
*/
revisionList: null,
/**
* @type {number}
*/
revisionWidth: 16,
/**
* @type {number}
*/
tooltipTimeout: -1,
/**
* @type {OO.ui.PopupWidget}
*/
currentTooltip: null,
/**
* @type {jQuery}
*/
currentTooltip: null,
$highlightedRevisionWrapper: null,
/**
* @param {number} revisionTickWidth
@ -33,60 +43,32 @@
revs = this.revisionList.getRevisions(),
maxChangeSizeLogged = Math.log( this.revisionList.getBiggestChangeSize() ),
self = this,
i, diffSize, tooltip, relativeChangeSize,
i, diffSize, relativeChangeSize,
showTooltip = function () {
self.showTooltip( $( this ) );
$( this ).tipsy( 'show' );
},
hideTooltip = function () {
self.hideTooltip( $( this ) );
},
tooltipGravity = function () {
// Returns a function setting a gravity of the tooltip so that it will be entirely visible
// Based on tipsy's own $.fn.tipsy.autoBounds, with considering the width of the
// inner contents of the tooltip, and assuming the gravity always starts with 'n'
return function () {
var dir = 'n',
$this = $( this ),
$tip = $this.tipsy( true ).$tip,
boundLeft = $( document ).scrollLeft() + $tip.outerWidth();
if ( $this.offset().left < boundLeft ) {
dir += 'w';
}
if ( $( window ).width() + $( document ).scrollLeft() - $this.offset().left < 0 ) {
dir += 'e';
}
return dir;
};
};
positionOffset = positionOffset || 0;
this.revisionWidth = revisionTickWidth;
for ( i = 0; i < revs.length; i++ ) {
diffSize = revs[ i ].getRelativeSize();
relativeChangeSize = diffSize !== 0 ? Math.ceil( 65.0 * Math.log( Math.abs( diffSize ) ) / maxChangeSizeLogged ) + 5 : 0;
tooltip = this.makeTooltip( revs[ i ] );
$html
.append( $( '<div>' )
.addClass( 'mw-revslider-revision-wrapper' )
.attr( 'title', tooltip )
.width( revisionTickWidth )
.tipsy( {
gravity: tooltipGravity(),
html: true,
trigger: 'manual',
className: 'mw-revslider-revision-tooltip mw-revslider-revision-tooltip-' + ( i + 1 )
} )
.width( this.revisionWidth )
.append( $( '<div>' )
.addClass( 'mw-revslider-revision' )
.attr( 'data-revid', revs[ i ].getId() )
.attr( 'data-pos', positionOffset + i + 1 )
.css( {
height: relativeChangeSize + 'px',
width: revisionTickWidth + 'px',
width: this.revisionWidth + 'px',
top: diffSize > 0 ? '-' + relativeChangeSize + 'px' : 0
} )
.addClass( diffSize > 0 ? 'mw-revslider-revision-up' : 'mw-revslider-revision-down' )
@ -123,10 +105,11 @@
* Hides the current tooltip immediately
*/
hideCurrentTooltip: function () {
if ( this.tooltipTimeout !== -1 ) {
if ( this.tooltipTimeout !== -1 && this.$highlightedRevisionWrapper !== null ) {
window.clearTimeout( this.tooltipTimeout );
this.currentTooltip.tipsy( 'hide' );
this.currentTooltip.removeClass( 'mw-revslider-revision-wrapper-hovered' );
this.$highlightedRevisionWrapper.removeClass( 'mw-revslider-revision-wrapper-hovered' );
this.currentTooltip.toggle( false );
this.currentTooltip.$element.remove();
}
},
@ -136,9 +119,15 @@
* @param {jQuery} $rev
*/
hideTooltip: function ( $rev ) {
var self = this;
this.tooltipTimeout = window.setTimeout( function () {
$rev.tipsy( 'hide' );
if ( $rev !== null ) {
$rev.removeClass( 'mw-revslider-revision-wrapper-hovered' );
}
if ( self.currentTooltip !== null && self.currentTooltip.isVisible() ) {
self.currentTooltip.toggle( false );
self.currentTooltip.$element.remove();
}
}, 500 );
},
@ -148,10 +137,40 @@
* @param {jQuery} $rev
*/
showTooltip: function ( $rev ) {
var pos = parseInt( $rev.find( '.mw-revslider-revision' ).attr( 'data-pos' ), 10 ),
revId = parseInt( $rev.find( '.mw-revslider-revision' ).attr( 'data-revid' ), 10 ),
revision = this.getRevisionWithId( revId ),
tooltip;
if ( revision === null ) {
return;
}
tooltip = this.makeTooltip( revision );
tooltip.$element.css( {
left: $rev.offset().left + this.revisionWidth / 2 + 'px',
top: $rev.offset().top + $rev.outerHeight() + 'px'
} );
tooltip.$element.attr( 'id', 'mw-revslider-revision-tooltip-' + pos );
$( 'body' ).append( tooltip.$element );
tooltip.toggle( true );
this.hideCurrentTooltip();
$rev.tipsy( 'show' );
$rev.addClass( 'mw-revslider-revision-wrapper-hovered' );
this.currentTooltip = $rev;
this.$highlightedRevisionWrapper = $rev;
this.currentTooltip = tooltip;
},
/**
* @param {number} revId
* @return {Revision|null}
*/
getRevisionWithId: function ( revId ) {
var matchedRevision = null;
this.revisionList.revisions.forEach( function ( revision ) {
if ( revision.getId() === revId ) {
matchedRevision = revision;
}
} );
return matchedRevision;
},
/**
@ -165,7 +184,7 @@
window.clearTimeout( self.tooltipTimeout );
} )
.on( 'mouseout', '.mw-revslider-revision-tooltip', function () {
self.hideTooltip( self.currentTooltip );
self.hideTooltip( self.$highlightedRevisionWrapper );
} );
},
@ -173,7 +192,7 @@
* Generates the HTML for a tooltip that appears on hover above each revision on the slider
*
* @param {Revision} rev
* @return {string}
* @return {OO.ui.PopupWidget}
*/
makeTooltip: function ( rev ) {
var $tooltip = $( '<div>' )
@ -188,7 +207,11 @@
this.makeChangeSizeLine( rev.getRelativeSize() ),
rev.isMinor() ? $( '<p>' ).text( mw.message( 'revisionslider-minoredit' ).text() ) : ''
);
return $tooltip.html();
return new OO.ui.PopupWidget( {
$content: $tooltip,
padded: true,
classes: [ 'mw-revslider-tooltip', 'mw-revslider-revision-tooltip' ]
} );
},
/**

View file

@ -71,6 +71,9 @@
.addClass( 'mw-revslider-revision-slider' )
.css( { direction: $container.css( 'direction' ) } ),
helpButton,
helpPopup,
backwardArrowPopup,
forwardArrowPopup,
self = this;
if ( $slider.css( 'direction' ) === 'rtl' ) {
@ -80,6 +83,13 @@
this.pointerOlder = new mw.libs.revisionSlider.Pointer( 'mw-revslider-pointer-older' );
this.pointerNewer = new mw.libs.revisionSlider.Pointer( 'mw-revslider-pointer-newer' );
helpPopup = new OO.ui.PopupWidget( {
$content: $( '<p>' ).text( mw.msg( 'revisionslider-show-help-tooltip' ) ),
padded: true,
width: 200,
classes: [ 'mw-revslider-tooltip', 'mw-revslider-help-tooltip' ]
} );
helpButton = new OO.ui.ButtonWidget( {
icon: 'help',
framed: false,
@ -89,13 +99,22 @@
.click( function () {
mw.libs.revisionSlider.HelpDialog.show();
} )
.tipsy( {
gravity: $( 'body' ).hasClass( 'ltr' ) ? 'se' : 'sw',
offset: 15,
title: function () {
return mw.msg( 'revisionslider-show-help-tooltip' );
}
.mouseover( { popup: helpPopup }, this.showPopup )
.mouseout( function () { helpPopup.toggle( false ); } );
backwardArrowPopup = new OO.ui.PopupWidget( {
$content: $( '<p>' ).text( mw.msg( 'revisionslider-arrow-tooltip-older' ) ),
padded: true,
width: 200,
classes: [ 'mw-revslider-tooltip', 'mw-revslider-arrow-tooltip' ]
} );
forwardArrowPopup = new OO.ui.PopupWidget( {
$content: $( '<p>' ).text( mw.msg( 'revisionslider-arrow-tooltip-newer' ) ),
padded: true,
width: 200,
classes: [ 'mw-revslider-tooltip', 'mw-revslider-arrow-tooltip' ]
} );
$( 'body' ).append( backwardArrowPopup.$element, forwardArrowPopup.$element, helpPopup.$element );
pointerContainerStyle = { left: pointerContainerPosition + 'px', width: pointerContainerWidth + 'px' };
if ( $slider.css( 'direction' ) === 'rtl' ) {
@ -113,14 +132,8 @@
$( '<a> ' )
.addClass( 'mw-revslider-arrow mw-revslider-arrow-backwards' )
.attr( 'data-dir', '-1' )
.tipsy( {
title: function () {
if ( $( this ).hasClass( 'mw-revslider-arrow-disabled' ) ) {
return '';
}
return mw.message( 'revisionslider-arrow-tooltip-older' ).text();
}
} ),
.mouseover( { popup: backwardArrowPopup }, this.showPopup )
.mouseout( { popup: backwardArrowPopup }, this.hidePopup ),
$( '<div>' )
.addClass( 'mw-revslider-revisions-container' )
.css( {
@ -130,21 +143,8 @@
$( '<a> ' )
.addClass( 'mw-revslider-arrow mw-revslider-arrow-forwards' )
.attr( 'data-dir', '1' )
.tipsy( {
gravity: function () {
if ( $slider.css( 'direction' ) === 'ltr' ) {
return Math.abs( window.innerWidth - this.getBoundingClientRect().right ) > 90 ? 'n' : 'ne';
} else {
return this.getBoundingClientRect().left > 90 ? 'n' : 'nw';
}
},
title: function () {
if ( $( this ).hasClass( 'mw-revslider-arrow-disabled' ) ) {
return '';
}
return mw.message( 'revisionslider-arrow-tooltip-newer' ).text();
}
} ),
.mouseover( { popup: forwardArrowPopup }, this.showPopup )
.mouseout( { popup: forwardArrowPopup }, this.hidePopup ),
helpButton.$element,
$( '<div>' ).css( { clear: 'both' } ),
$( '<div>' )
@ -256,6 +256,23 @@
this.diffPage.initOnPopState( this );
},
showPopup: function ( e ) {
var popup = e.data.popup;
if ( $( this ).hasClass( 'mw-revslider-arrow-disabled' ) ) {
return;
}
popup.$element.css( {
left: $( this ).offset().left + $( this ).outerWidth() / 2 + 'px',
top: $( this ).offset().top + $( this ).outerHeight() + 'px'
} );
popup.toggle( true );
},
hidePopup: function ( e ) {
var popup = e.data.popup;
popup.toggle( false );
},
revisionWrapperClickHandler: function ( e ) {
var $revWrap = $( this ),
view = e.data.view,

View file

@ -85,8 +85,8 @@
border-left: solid 1px #fff;
}
.mw-revslider-revision-tooltip .tipsy-inner {
max-width: none;
.mw-revslider-tooltip {
font-size: 0.8em;
}
.mw-revslider-revision-tooltip .mw-revslider-change-positive {
@ -101,6 +101,11 @@
color: #aaa;
}
.mw-revslider-arrow-tooltip,
.mw-revslider-help-tooltip {
text-align: center;
}
.mw-revslider-revisions {
height: 140px;
white-space: nowrap;

View file

@ -26,7 +26,7 @@ class DiffPage
end
def revisionslider_tooltip(index = 1)
element('div', css: '.mw-revslider-revision-tooltip-' + index.to_s)
element('div', id: 'mw-revslider-revision-tooltip-' + index.to_s)
end

View file

@ -37,18 +37,20 @@
user: 'User1',
minor: true
} ),
$tooltipHtml;
tooltip,
tooltipHtml;
revision.setRelativeSize( 210 );
mw.libs.revisionSlider.userOffset = 0;
$tooltipHtml = revisionListView.makeTooltip( revision );
tooltip = revisionListView.makeTooltip( revision );
tooltipHtml = tooltip.$element.html();
assert.ok( $tooltipHtml.match( /User1/ ), 'Test the user.' );
assert.ok( $tooltipHtml.match( /Hello/ ), 'Test the comment.' );
assert.ok( $tooltipHtml.match( /230/ ), 'Test the page size.' );
assert.ok( $tooltipHtml.match( /\+210/ ), 'Test the change size.' );
assert.ok( tooltipHtml.match( /User1/ ), 'Test the user.' );
assert.ok( tooltipHtml.match( /Hello/ ), 'Test the comment.' );
assert.ok( tooltipHtml.match( /230/ ), 'Test the page size.' );
assert.ok( tooltipHtml.match( /\+210/ ), 'Test the change size.' );
} );
QUnit.revisionSlider.testOrSkip( 'tooltip is composed correctly with en lang', function ( assert ) {
@ -62,20 +64,22 @@
user: 'User1',
minor: true
} ),
$tooltipHtml;
tooltip,
tooltipHtml;
revision.setRelativeSize( 2100 );
mw.libs.revisionSlider.userOffset = 0;
$tooltipHtml = revisionListView.makeTooltip( revision );
tooltip = revisionListView.makeTooltip( revision );
tooltipHtml = tooltip.$element.html();
assert.ok( $tooltipHtml.match( /User1/ ), 'Test the user.' );
assert.ok( $tooltipHtml.match( /Hello/ ), 'Test the comment.' );
assert.ok( $tooltipHtml.match( /2,300/ ), 'Test the page size.' );
assert.ok( $tooltipHtml.match( /\+2,100/ ), 'Test the change size.' );
assert.ok( $tooltipHtml.match( /10:27, 26 Apr 2016/ ), 'Test the date.' );
assert.ok( $tooltipHtml.match( /minor/ ), 'Test minor.' );
assert.ok( tooltipHtml.match( /User1/ ), 'Test the user.' );
assert.ok( tooltipHtml.match( /Hello/ ), 'Test the comment.' );
assert.ok( tooltipHtml.match( /2,300/ ), 'Test the page size.' );
assert.ok( tooltipHtml.match( /\+2,100/ ), 'Test the change size.' );
assert.ok( tooltipHtml.match( /10:27, 26 Apr 2016/ ), 'Test the date.' );
assert.ok( tooltipHtml.match( /minor/ ), 'Test minor.' );
}, mw.config.get( 'wgUserLanguage' ) !== 'en' );
QUnit.test( 'empty user leads to no user line', function ( assert ) {