jenkins-bot has submitted this change and it was merged.

Change subject: Upstream `isElementInViewport` from MobileFrontend
......................................................................


Upstream `isElementInViewport` from MobileFrontend

As a useful utility function, we've copied this method several times
across multiple extensions, which is a pretty good sign it should
actually live in core.

Changes:
 * Add `mediawiki.viewport` module
 * Rewrite method to be more robust and accept any viewport
 * Add `mw.viewport` to jsduck categories file
 * Add method for checking if an element is close to the viewport
 * Add unit tests

Bug: T124317
Change-Id: I38eec4f1e568f51e7e212b2b3f10b8da8d36f316
---
M maintenance/jsduck/categories.json
M resources/Resources.php
A resources/src/mediawiki/mediawiki.viewport.js
M tests/qunit/QUnitTestResources.php
A tests/qunit/suites/resources/mediawiki/mediawiki.viewport.test.js
5 files changed, 187 insertions(+), 1 deletion(-)

Approvals:
  Jdlrobson: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/maintenance/jsduck/categories.json 
b/maintenance/jsduck/categories.json
index 41b56f6..d9e2c50 100644
--- a/maintenance/jsduck/categories.json
+++ b/maintenance/jsduck/categories.json
@@ -32,7 +32,8 @@
                                        "mw.util",
                                        "mw.plugin.*",
                                        "mw.cookie",
-                                       "mw.experiments"
+                                       "mw.experiments",
+                                       "mw.viewport"
                                ]
                        },
                        {
diff --git a/resources/Resources.php b/resources/Resources.php
index 1179a9a..8b6b559 100644
--- a/resources/Resources.php
+++ b/resources/Resources.php
@@ -1334,6 +1334,11 @@
                'position' => 'top', // For $wgPreloadJavaScriptMwUtil
                'targets' => [ 'desktop', 'mobile' ],
        ],
+       'mediawiki.viewport' => [
+               'scripts' => 'resources/src/mediawiki/mediawiki.viewport.js',
+               'position' => 'top',
+               'targets' => [ 'desktop', 'mobile' ],
+       ],
        'mediawiki.checkboxtoggle' => [
                'scripts' => 
'resources/src/mediawiki/mediawiki.checkboxtoggle.js',
                'position' => 'top',
diff --git a/resources/src/mediawiki/mediawiki.viewport.js 
b/resources/src/mediawiki/mediawiki.viewport.js
new file mode 100644
index 0000000..aa9dd05
--- /dev/null
+++ b/resources/src/mediawiki/mediawiki.viewport.js
@@ -0,0 +1,89 @@
+( function ( mw, $ ) {
+       'use strict';
+
+       /**
+        * Utility library for viewport-related functions
+        *
+        * Notable references:
+        * - https://github.com/tuupola/jquery_lazyload
+        * - https://github.com/luis-almeida/unveil
+        *
+        * @class mw.viewport
+        * @singleton
+        */
+       var viewport = {
+
+               /**
+                * This is a private method pulled inside the module for 
testing purposes.
+                *
+                * @ignore
+                * @private
+                */
+               makeViewportFromWindow: function () {
+                       var $window = $( window ),
+                               scrollTop = $window.scrollTop(),
+                               scrollLeft = $window.scrollLeft();
+
+                       return {
+                               top: scrollTop,
+                               left: scrollLeft,
+                               right: scrollLeft + $window.width(),
+                               bottom: ( window.innerHeight ? 
window.innerHeight : $window.height() ) + scrollTop
+                       };
+               },
+
+               /**
+                * Check if any part of a given element is in a given viewport
+                *
+                * @method
+                * @param {HTMLElement} el Element that's being tested
+                * @param {Object} [rectangle] Viewport to test against; 
structured as such:
+                *
+                *      var rectangle = {
+                *              top: topEdge,
+                *              left: leftEdge,
+                *              right: rightEdge,
+                *              bottom: bottomEdge
+                *      }
+                *      Defaults to viewport made from `window`.
+                *
+                * @return {boolean}
+                */
+               isElementInViewport: function ( el, rectangle ) {
+                       var elRect = el.getBoundingClientRect(),
+                               viewport = rectangle || 
this.makeViewportFromWindow();
+
+                       return (
+                               ( viewport.bottom >= elRect.top ) &&
+                               ( viewport.right >= elRect.left ) &&
+                               ( viewport.top <= elRect.top + elRect.height ) 
&&
+                               ( viewport.left <= elRect.left + elRect.width )
+                       );
+               },
+
+               /**
+                * Check if an element is a given threshold away in any 
direction from a given viewport
+                *
+                * @method
+                * @param {HTMLElement} el Element that's being tested
+                * @param {number} [threshold] Pixel distance considered 
"close". Must be a positive number.
+                *  Defaults to 50.
+                * @param {Object} [rectangle] Viewport to test against.
+                *  Defaults to viewport made from `window`.
+                * @return {boolean}
+                */
+               isElementCloseToViewport: function ( el, threshold, rectangle ) 
{
+                       var viewport = rectangle ? $.extend( {}, rectangle ) : 
this.makeViewportFromWindow();
+                       threshold = threshold || 50 ;
+
+                       viewport.top -= threshold;
+                       viewport.left -= threshold;
+                       viewport.right += threshold;
+                       viewport.bottom += threshold;
+                       return this.isElementInViewport( el, viewport );
+               }
+
+       };
+
+       mw.viewport = viewport;
+}( mediaWiki, jQuery ) );
diff --git a/tests/qunit/QUnitTestResources.php 
b/tests/qunit/QUnitTestResources.php
index a2dead6..310268f 100644
--- a/tests/qunit/QUnitTestResources.php
+++ b/tests/qunit/QUnitTestResources.php
@@ -80,6 +80,7 @@
                        
'tests/qunit/suites/resources/mediawiki/mediawiki.Uri.test.js',
                        
'tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js',
                        
'tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js',
+                       
'tests/qunit/suites/resources/mediawiki/mediawiki.viewport.test.js',
                        
'tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js',
                        
'tests/qunit/suites/resources/mediawiki.api/mediawiki.api.category.test.js',
                        
'tests/qunit/suites/resources/mediawiki.api/mediawiki.api.messages.test.js',
@@ -130,6 +131,7 @@
                        'mediawiki.template.mustache',
                        'mediawiki.template',
                        'mediawiki.util',
+                       'mediawiki.viewport',
                        'mediawiki.special.recentchanges',
                        'mediawiki.language',
                        'mediawiki.cldr',
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.viewport.test.js 
b/tests/qunit/suites/resources/mediawiki/mediawiki.viewport.test.js
new file mode 100644
index 0000000..61391d8
--- /dev/null
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.viewport.test.js
@@ -0,0 +1,89 @@
+( function ( mw, $ ) {
+
+       // Simulate square element with 20px long edges placed at (20, 20) on 
the page
+       var
+               DEFAULT_VIEWPORT = {
+                       top: 0,
+                       left: 0,
+                       right: 100,
+                       bottom: 100
+               };
+
+       QUnit.module( 'mediawiki.viewport', QUnit.newMwEnvironment( {
+               setup: function () {
+                       this.el = $( '<div />' )
+                               .appendTo( '#qunit-fixture' )
+                               .width( 20 )
+                               .height( 20 )
+                               .offset( {
+                                       top: 20,
+                                       left: 20
+                               } )
+                               .get( 0 );
+                       this.sandbox.stub( mw.viewport, 
'makeViewportFromWindow' )
+                               .returns( DEFAULT_VIEWPORT );
+               }
+       } ) );
+
+       QUnit.test( 'isElementInViewport', 6, function ( assert ) {
+               var viewport = $.extend( {}, DEFAULT_VIEWPORT );
+               assert.ok( mw.viewport.isElementInViewport( this.el, viewport ),
+                       'It should return true when the element is fully 
enclosed in the viewport' );
+
+               viewport.right = 20;
+               viewport.bottom = 20;
+               assert.ok( mw.viewport.isElementInViewport( this.el, viewport ),
+                       'It should return true when only the top-left of the 
element is within the viewport' );
+
+               viewport.top = 40;
+               viewport.left = 40;
+               viewport.right = 50;
+               viewport.bottom = 50;
+               assert.ok( mw.viewport.isElementInViewport( this.el, viewport ),
+                       'It should return true when only the bottom-right is 
within the viewport' );
+
+               viewport.top = 30;
+               viewport.left = 30;
+               viewport.right = 35;
+               viewport.bottom = 35;
+               assert.ok( mw.viewport.isElementInViewport( this.el, viewport ),
+                       'It should return true when the element encapsulates 
the viewport' );
+
+               viewport.top = 0;
+               viewport.left = 0;
+               viewport.right = 19;
+               viewport.bottom = 19;
+               assert.notOk( mw.viewport.isElementInViewport( this.el, 
viewport ),
+                       'It should return false when the element is not within 
the viewport' );
+
+               assert.ok( mw.viewport.isElementInViewport( this.el ),
+                       'It should default to the window object if no viewport 
is given' );
+       } );
+
+       QUnit.test( 'isElementCloseToViewport', 3, function ( assert ) {
+               var
+                       viewport = {
+                               top: 90,
+                               left: 90,
+                               right: 100,
+                               bottom: 100
+                       },
+                       distantElement = $( '<div />' )
+                               .appendTo( '#qunit-fixture' )
+                               .width( 20 )
+                               .height( 20 )
+                               .offset( {
+                                       top: 220,
+                                       left: 20
+                               } )
+                               .get( 0 );
+
+               assert.ok( mw.viewport.isElementCloseToViewport( this.el, 60, 
viewport ),
+                       'It should return true when the element is within the 
given threshold away' );
+               assert.notOk( mw.viewport.isElementCloseToViewport( this.el, 
20, viewport ),
+                       'It should return false when the element is further 
than the given threshold away' );
+               assert.notOk( mw.viewport.isElementCloseToViewport( 
distantElement ),
+                       'It should default to a threshold of 50px and the 
window\'s viewport' );
+       } );
+
+}( mediaWiki, jQuery ) );

-- 
To view, visit https://gerrit.wikimedia.org/r/267058
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I38eec4f1e568f51e7e212b2b3f10b8da8d36f316
Gerrit-PatchSet: 15
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Jhobs <[email protected]>
Gerrit-Reviewer: Bmansurov <[email protected]>
Gerrit-Reviewer: Edokter <[email protected]>
Gerrit-Reviewer: Florianschmidtwelzow <[email protected]>
Gerrit-Reviewer: Jack Phoenix <[email protected]>
Gerrit-Reviewer: Jdlrobson <[email protected]>
Gerrit-Reviewer: Jhobs <[email protected]>
Gerrit-Reviewer: Krinkle <[email protected]>
Gerrit-Reviewer: Phuedx <[email protected]>
Gerrit-Reviewer: TheDJ <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to