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

Change subject: Allow measuring impact of section collapsing in 
Schema:MobileWebSectionUsage
......................................................................


Allow measuring impact of section collapsing in Schema:MobileWebSectionUsage

* Update schema revision to 15038458;
* Trigger "scrolled-into-view" event when a heading is
   scrolled into the user's viewport or is already in it;
* 50% of the sampled users will be bucketed as group A;
* Users in the A group will see sections expanded by default in stable
  and while on a small screen;
* Make isUserInBucket method of the Schema public (update tests)

This experiment must be enabled via
$wgExperiments['sectionCollapsing']['enabled] = true

Bug: T120292
Change-Id: I91f82ed3c1bc7dcd70305a7e0baebd5e8b77068c
---
M includes/MobileFrontend.hooks.php
M includes/Resources.php
M includes/config/Experimental.php
M resources/mobile.startup/Schema.js
M resources/mobile.toggle/toggle.js
M tests/qunit/mobile.startup/test_Schema.js
6 files changed, 92 insertions(+), 14 deletions(-)

Approvals:
  BryanDavis: Looks good to me, but someone else must approve
  Jdlrobson: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/includes/MobileFrontend.hooks.php 
b/includes/MobileFrontend.hooks.php
index 383d838..f2936e2 100644
--- a/includes/MobileFrontend.hooks.php
+++ b/includes/MobileFrontend.hooks.php
@@ -996,7 +996,7 @@
                        'MobileWebDiffClickTracking' => 10720373,
                        'MobileWebMainMenuClickTracking' => 11568715,
                        'MobileWebSearch' => 12054448,
-                       'MobileWebSectionUsage' => 14321266,
+                       'MobileWebSectionUsage' => 15038458,
                        'MobileWebUIClickTracking' => 10742159,
                        'MobileWebWatching' => 11761466,
                        'MobileWebWatchlistClickTracking' => 10720361,
diff --git a/includes/Resources.php b/includes/Resources.php
index 591b057..5bdd878 100644
--- a/includes/Resources.php
+++ b/includes/Resources.php
@@ -922,7 +922,7 @@
        'mobile.toggle' => $wgMFResourceFileModuleBoilerplate + array(
                'dependencies' => array(
                        'mobile.settings',
-                       // uses util.js
+                       // uses util.js and jquery.throttle-debounce
                        'mobile.startup',
                ),
                'styles' => array(
diff --git a/includes/config/Experimental.php b/includes/config/Experimental.php
index f230e1c..f9dbd8f 100644
--- a/includes/config/Experimental.php
+++ b/includes/config/Experimental.php
@@ -28,6 +28,15 @@
                        'A' => 0.03,
                ),
        ),
+       // A/B test that measures the impact of section collapsing by default
+       'sectionCollapsing' => array(
+               'name' => 'sectionCollapsing',
+               'enabled' => false,
+               'buckets' => array(
+                       'control' => 0.5,
+                       'A' => 0.5,
+               ),
+       ),
 );
 
 /**
diff --git a/resources/mobile.startup/Schema.js 
b/resources/mobile.startup/Schema.js
index fe72c39..bcddb0d 100644
--- a/resources/mobile.startup/Schema.js
+++ b/resources/mobile.startup/Schema.js
@@ -82,9 +82,8 @@
                /**
                 * Whether the user is bucketed.
                 * @returns {Boolean}
-                * @private
                 */
-               _isUserInBucket: function () {
+               isUserInBucket: function () {
                        if ( mw.config.get( 'wgMFIgnoreEventLoggingBucketing' ) 
) {
                                return true;
                        } else if ( this._isInBucket === undefined ) {
@@ -127,7 +126,7 @@
                        var deferred = $.Deferred();
 
                        // Log event if logging schema is not sampled or if 
user is in the bucket
-                       if ( !this.isSampled || this._isUserInBucket() ) {
+                       if ( !this.isSampled || this.isUserInBucket() ) {
                                mw.track( 'event.' + this.name, $.extend( {}, 
this.defaults, data ) );
 
                                return deferred.resolve();
diff --git a/resources/mobile.toggle/toggle.js 
b/resources/mobile.toggle/toggle.js
index ad51223..ded4d24 100644
--- a/resources/mobile.toggle/toggle.js
+++ b/resources/mobile.toggle/toggle.js
@@ -11,7 +11,8 @@
                        name: 'arrow',
                        additionalClassNames: 'indicator'
                },
-               Icon = M.require( 'mobile.startup/Icon' );
+               Icon = M.require( 'mobile.startup/Icon' ),
+               $window = $( window );
 
        /**
         * A class for enabling toggling
@@ -24,10 +25,10 @@
         * @extends OO.EventEmitter
         */
        function Toggler( $container, prefix, page, schema ) {
+               this.schema = schema;
                OO.EventEmitter.call( this );
                this._enable( $container, prefix, page );
                if ( schema ) {
-                       this.schema = schema;
                        this.connect( this, {
                                toggled: 'onToggle'
                        } );
@@ -239,10 +240,13 @@
         * @private
         */
        Toggler.prototype._enable = function ( $container, prefix, page ) {
-               var tagName, expandSections, indicator,
+               var tagName, expandSections, indicator, inSample,
                        $firstHeading,
                        self = this,
-                       collapseSectionsByDefault = mw.config.get( 
'wgMFCollapseSectionsByDefault' );
+                       collapseSectionsByDefault = mw.config.get( 
'wgMFCollapseSectionsByDefault' ),
+                       shouldLogScrolledIntoView = false,
+                       experiments = mw.config.get( 'wgMFExperiments' ),
+                       isTestA = false;
 
                // Also allow .section-heading if some extensions like Wikibase
                // want to toggle other headlines than direct descendants of 
$container.
@@ -255,6 +259,20 @@
                }
                expandSections = !collapseSectionsByDefault ||
                        ( context.isBetaGroupMember() && settings.get( 
'expandSections', true ) === 'true' );
+
+               // A/B test to measure the impact of section collapsing
+               if ( self.schema && self.schema.isUserInBucket() && 
experiments.sectionCollapsing ) {
+                       inSample = mw.experiments.getBucket( 
experiments.sectionCollapsing, mw.user.sessionId() ) === 'A';
+
+                       // Bucketed users who are in stable and using a small 
screen device
+                       // will see expanded sections by default
+                       // @see T120292
+                       if ( inSample && context.getMode() === 'stable' && 
!browser.isWideScreen() ) {
+                               expandSections = true;
+                               shouldLogScrolledIntoView = true;
+                               isTestA = true;
+                       }
+               }
 
                $container.find( tagName ).each( function ( i ) {
                        var $heading = $( this ),
@@ -298,7 +316,7 @@
                                        // Expand sections by default on wide 
screen devices or if the expand sections setting is set
                                        self.toggle.call( self, $heading );
                                }
-
+                               logWhenHeadingIsScrolledIntoView( $heading, i, 
isTestA );
                        }
                } );
 
@@ -332,6 +350,58 @@
                        }
                }
 
+               /**
+                * Check if at least half of the element's height and half of 
its width are in viewport
+                *
+                * @method
+                * @param {jQuery.Object} $el - element that's being tested
+                * @return {Boolean}
+                */
+               function isElementInViewport( $el ) {
+                       var windowHeight = $window.height(),
+                               windowWidth = $window.width(),
+                               windowScrollLeft = $window.scrollLeft(),
+                               windowScrollTop = $window.scrollTop(),
+                               elHeight = $el.height(),
+                               elWidth = $el.width(),
+                               elOffset = $el.offset();
+
+                       return (
+                               ( windowScrollTop + windowHeight >= 
elOffset.top + elHeight / 2 ) &&
+                               ( windowScrollLeft + windowWidth >= 
elOffset.left + elWidth / 2 ) &&
+                               ( windowScrollTop <= elOffset.top + elHeight / 
2 )
+                       );
+               }
+
+               /**
+                * Log when the heading is scrolled into the viewport
+                *
+                * @param {jQuery.Object} $heading
+                * @param {Number} sectionId that was scrolled into the viewport
+                * @param {Boolean} isTestA whether the user is in the A bucket
+                */
+               function logWhenHeadingIsScrolledIntoView( $heading, sectionId, 
isTestA ) {
+                       /**
+                        * Log when a heading is seen by the user
+                        * @ignore
+                        */
+                       function log() {
+                               if ( isElementInViewport( $heading ) ) {
+                                       $window.off( 'scroll.' + $heading.attr( 
'id' ), log );
+                                       self.schema.log( {
+                                               eventName: 'scrolled-into-view',
+                                               isTestA: isTestA,
+                                               section: sectionId
+                                       } );
+                               }
+                       }
+
+                       if ( self.schema ) {
+                               $window.on( 'scroll.' + $heading.attr( 'id' ), 
$.debounce( 250, log ) );
+                               log();
+                       }
+               }
+
                checkInternalRedirectAndHash();
                checkHash( this );
                // Restricted to links created by editors and thus outside our 
control
diff --git a/tests/qunit/mobile.startup/test_Schema.js 
b/tests/qunit/mobile.startup/test_Schema.js
index b3b36cf..97d1232 100644
--- a/tests/qunit/mobile.startup/test_Schema.js
+++ b/tests/qunit/mobile.startup/test_Schema.js
@@ -53,13 +53,13 @@
 
                // Default sampling rate is 0.5, isSampled is true, and 
Math.random returns 0.4
                this.sandbox.stub( Math, 'random' ).returns( 0.4 );
-               assert.strictEqual( testSchema._isUserInBucket(), true, 'user 
is in bucket' );
+               assert.strictEqual( testSchema.isUserInBucket(), true, 'user is 
in bucket' );
 
                // Default sampling rate is 0.5, isSampled is true, and 
Math.random returns 0.6
                testSchema = new TestSchema();
                Math.random.restore();
                this.sandbox.stub( Math, 'random' ).returns( 0.6 );
-               assert.strictEqual( testSchema._isUserInBucket(), false, 'user 
is not in bucket' );
+               assert.strictEqual( testSchema.isUserInBucket(), false, 'user 
is not in bucket' );
 
                // Default sampling rate is 0.5, isSampled is false (default), 
and Math.random returns 0.4
                TestSchema = Schema.extend( {
@@ -68,7 +68,7 @@
                testSchema = new TestSchema();
                Math.random.restore();
                this.sandbox.stub( Math, 'random' ).returns( 0.4 );
-               assert.strictEqual( testSchema._isUserInBucket(), false, 'user 
is not in bucket' );
+               assert.strictEqual( testSchema.isUserInBucket(), false, 'user 
is not in bucket' );
        } );
 
        QUnit.test( '#log', 2, function ( assert ) {
@@ -90,7 +90,7 @@
                } );
 
                schema.isSampled = true;
-               this.sandbox.stub( schema, '_isUserInBucket' ).returns( false );
+               this.sandbox.stub( schema, 'isUserInBucket' ).returns( false );
 
                schema.log( event ).fail( function () {
                        assert.strictEqual(

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

Gerrit-MessageType: merged
Gerrit-Change-Id: I91f82ed3c1bc7dcd70305a7e0baebd5e8b77068c
Gerrit-PatchSet: 6
Gerrit-Project: mediawiki/extensions/MobileFrontend
Gerrit-Branch: master
Gerrit-Owner: Bmansurov <[email protected]>
Gerrit-Reviewer: Bmansurov <[email protected]>
Gerrit-Reviewer: BryanDavis <[email protected]>
Gerrit-Reviewer: Jdlrobson <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

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

Reply via email to