Compare commits
2 Commits
master
...
wmf/1.39.0
Author | SHA1 | Date |
---|---|---|
Jan Drewniak | 4c52b69780 | |
bwang | 8772b0065e |
|
@ -3,6 +3,11 @@
|
||||||
const EXCLUDED_BUCKET = 'unsampled';
|
const EXCLUDED_BUCKET = 'unsampled';
|
||||||
const TREATMENT_BUCKET_SUBSTRING = 'treatment';
|
const TREATMENT_BUCKET_SUBSTRING = 'treatment';
|
||||||
const WEB_AB_TEST_ENROLLMENT_HOOK = 'mediawiki.web_AB_test_enrollment';
|
const WEB_AB_TEST_ENROLLMENT_HOOK = 'mediawiki.web_AB_test_enrollment';
|
||||||
|
/**
|
||||||
|
* @typedef {Function} TreatmentBucketFunction
|
||||||
|
* @param {string} [a]
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} WebABTest
|
* @typedef {Object} WebABTest
|
||||||
|
@ -10,7 +15,7 @@ const WEB_AB_TEST_ENROLLMENT_HOOK = 'mediawiki.web_AB_test_enrollment';
|
||||||
* @property {function(): string} getBucket
|
* @property {function(): string} getBucket
|
||||||
* @property {function(string): boolean} isInBucket
|
* @property {function(string): boolean} isInBucket
|
||||||
* @property {function(): boolean} isInSample
|
* @property {function(): boolean} isInSample
|
||||||
* @property {function(): boolean} isInTreatmentBucket
|
* @property {TreatmentBucketFunction} isInTreatmentBucket
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,12 +46,15 @@ const WEB_AB_TEST_ENROLLMENT_HOOK = 'mediawiki.web_AB_test_enrollment';
|
||||||
* name: 'nameOfExperiment',
|
* name: 'nameOfExperiment',
|
||||||
* buckets: {
|
* buckets: {
|
||||||
* unsampled: {
|
* unsampled: {
|
||||||
* samplingRate: 0.5
|
* samplingRate: 0.25
|
||||||
* },
|
* },
|
||||||
* control: {
|
* control: {
|
||||||
* samplingRate: 0.25
|
* samplingRate: 0.25
|
||||||
* },
|
* },
|
||||||
* treatment: {
|
* treatment1: {
|
||||||
|
* samplingRate: 0.25
|
||||||
|
* },
|
||||||
|
* treatment2: {
|
||||||
* samplingRate: 0.25
|
* samplingRate: 0.25
|
||||||
* }
|
* }
|
||||||
* },
|
* },
|
||||||
|
@ -147,17 +155,18 @@ module.exports = function webABTest( props ) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not the subject has been bucketed in a treatment bucket as
|
* Whether or not the subject has been bucketed in a treatment bucket as
|
||||||
* defined by the bucket name containing the case-insensitive `treatment`
|
* defined by the bucket name containing the case-insensitive 'treatment',
|
||||||
* substring (e.g. 'treatment', 'sticky-header-treatment' and
|
* 'treatment1', or 'treatment2' substring (e.g. 'treatment', 'treatment1',
|
||||||
* 'stickyHeaderTreatment' are all assumed to be treatment buckets).
|
* 'sticky-header-treatment1' and 'stickyHeaderTreatment2' are all assumed
|
||||||
|
* to be treatment buckets).
|
||||||
*
|
*
|
||||||
|
* @param {string|null} [treatmentBucketName] lowercase name of bucket.
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
function isInTreatmentBucket() {
|
function isInTreatmentBucket( treatmentBucketName = '' ) {
|
||||||
const bucket = getBucket();
|
const bucket = getBucket();
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
return bucket.toLowerCase().includes( TREATMENT_BUCKET_SUBSTRING );
|
return bucket.toLowerCase().includes( `${TREATMENT_BUCKET_SUBSTRING}${treatmentBucketName}` );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -8,6 +8,7 @@ const
|
||||||
initTableOfContents = require( './tableOfContents.js' ),
|
initTableOfContents = require( './tableOfContents.js' ),
|
||||||
deferUntilFrame = require( './deferUntilFrame.js' ),
|
deferUntilFrame = require( './deferUntilFrame.js' ),
|
||||||
ABTestConfig = require( /** @type {string} */ ( './config.json' ) ).wgVectorWebABTestEnrollment || {},
|
ABTestConfig = require( /** @type {string} */ ( './config.json' ) ).wgVectorWebABTestEnrollment || {},
|
||||||
|
stickyHeaderEditIconConfig = require( /** @type {string} */ ( './config.json' ) ).wgVectorStickyHeaderEdit || true,
|
||||||
TOC_ID = 'mw-panel-toc',
|
TOC_ID = 'mw-panel-toc',
|
||||||
TOC_ID_LEGACY = 'toc',
|
TOC_ID_LEGACY = 'toc',
|
||||||
BODY_CONTENT_ID = 'bodyContent',
|
BODY_CONTENT_ID = 'bodyContent',
|
||||||
|
@ -58,7 +59,7 @@ const getHeadingIntersectionHandler = ( changeActiveSection ) => {
|
||||||
function initStickyHeaderABTests( abConfig, isStickyHeaderFeatureAllowed, getEnabledExperiment ) {
|
function initStickyHeaderABTests( abConfig, isStickyHeaderFeatureAllowed, getEnabledExperiment ) {
|
||||||
let show = isStickyHeaderFeatureAllowed,
|
let show = isStickyHeaderFeatureAllowed,
|
||||||
stickyHeaderExperiment,
|
stickyHeaderExperiment,
|
||||||
noEditIcons = true;
|
noEditIcons = stickyHeaderEditIconConfig;
|
||||||
|
|
||||||
// One of the sticky header AB tests is specified in the config
|
// One of the sticky header AB tests is specified in the config
|
||||||
const abTestName = abConfig.name,
|
const abTestName = abConfig.name,
|
||||||
|
@ -74,16 +75,21 @@ function initStickyHeaderABTests( abConfig, isStickyHeaderFeatureAllowed, getEna
|
||||||
// If eligible, initialize the AB test
|
// If eligible, initialize the AB test
|
||||||
stickyHeaderExperiment = getEnabledExperiment( abConfig );
|
stickyHeaderExperiment = getEnabledExperiment( abConfig );
|
||||||
|
|
||||||
// If running initial AB test, only show sticky header to treatment group.
|
// If running initial or edit AB test, show sticky header to treatment groups
|
||||||
if ( abTestName === stickyHeader.STICKY_HEADER_EXPERIMENT_NAME ) {
|
// only. Unsampled and control buckets do not see sticky header.
|
||||||
|
if ( abTestName === stickyHeader.STICKY_HEADER_EXPERIMENT_NAME ||
|
||||||
|
abTestName === stickyHeader.STICKY_HEADER_EDIT_EXPERIMENT_NAME
|
||||||
|
) {
|
||||||
show = stickyHeaderExperiment.isInTreatmentBucket();
|
show = stickyHeaderExperiment.isInTreatmentBucket();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If running edit-button AB test, show sticky header to all buckets
|
// If running edit-button AB test, the edit buttons in sticky header are shown
|
||||||
// and show edit button for treatment group
|
// to second treatment group only.
|
||||||
if ( abTestName === stickyHeader.STICKY_HEADER_EDIT_EXPERIMENT_NAME ) {
|
if ( abTestName === stickyHeader.STICKY_HEADER_EDIT_EXPERIMENT_NAME ) {
|
||||||
show = true;
|
if ( stickyHeaderExperiment.isInTreatmentBucket( '1' ) ) {
|
||||||
if ( stickyHeaderExperiment.isInTreatmentBucket() ) {
|
noEditIcons = true;
|
||||||
|
}
|
||||||
|
if ( stickyHeaderExperiment.isInTreatmentBucket( '2' ) ) {
|
||||||
noEditIcons = false;
|
noEditIcons = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,7 +102,9 @@
|
||||||
|
|
||||||
@media ( min-width: @min-width-desktop-wide ) {
|
@media ( min-width: @min-width-desktop-wide ) {
|
||||||
.mw-page-container-inner {
|
.mw-page-container-inner {
|
||||||
grid-template-columns: ~'284px @{width-gutter} 1fr';
|
/* Use of minmax is important to restrict the maximum grid column width
|
||||||
|
more information: T314756 */
|
||||||
|
grid-template-columns: ~'284px @{width-gutter} minmax(0, 1fr)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ describe( 'main.js', () => {
|
||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
isUserInTreatmentBucket: false,
|
isUserInTreatmentBucket: false,
|
||||||
expectedResult: {
|
expectedResult: {
|
||||||
showStickyHeader: true,
|
showStickyHeader: false,
|
||||||
disableEditIcons: true
|
disableEditIcons: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -78,7 +78,7 @@ describe( 'main.js', () => {
|
||||||
{
|
{
|
||||||
abConfig: STICKY_HEADER_AB,
|
abConfig: STICKY_HEADER_AB,
|
||||||
isEnabled: false, // sticky header unavailable
|
isEnabled: false, // sticky header unavailable
|
||||||
isUserInTreatmentBucket: false, // not in treament bucket
|
isUserInTreatmentBucket: false, // not in treatment bucket
|
||||||
expectedResult: {
|
expectedResult: {
|
||||||
showStickyHeader: false,
|
showStickyHeader: false,
|
||||||
disableEditIcons: true
|
disableEditIcons: true
|
||||||
|
@ -87,7 +87,7 @@ describe( 'main.js', () => {
|
||||||
{
|
{
|
||||||
abConfig: STICKY_HEADER_AB,
|
abConfig: STICKY_HEADER_AB,
|
||||||
isEnabled: true, // sticky header available
|
isEnabled: true, // sticky header available
|
||||||
isUserInTreatmentBucket: false, // not in treament bucket
|
isUserInTreatmentBucket: false, // not in treatment bucket
|
||||||
expectedResult: {
|
expectedResult: {
|
||||||
showStickyHeader: false,
|
showStickyHeader: false,
|
||||||
disableEditIcons: true
|
disableEditIcons: true
|
||||||
|
@ -96,7 +96,7 @@ describe( 'main.js', () => {
|
||||||
{
|
{
|
||||||
abConfig: STICKY_HEADER_AB,
|
abConfig: STICKY_HEADER_AB,
|
||||||
isEnabled: false, // sticky header is not available
|
isEnabled: false, // sticky header is not available
|
||||||
isUserInTreatmentBucket: true, // but the user is in the treament bucket
|
isUserInTreatmentBucket: true, // but the user is in the treatment bucket
|
||||||
expectedResult: {
|
expectedResult: {
|
||||||
showStickyHeader: false,
|
showStickyHeader: false,
|
||||||
disableEditIcons: true
|
disableEditIcons: true
|
||||||
|
@ -129,27 +129,33 @@ describe( 'main.js', () => {
|
||||||
disableEditIcons: false
|
disableEditIcons: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
].forEach( ( { abConfig, isEnabled, isUserInTreatmentBucket, expectedResult } ) => {
|
].forEach(
|
||||||
document.documentElement.classList.add( 'vector-sticky-header-enabled' );
|
( {
|
||||||
const result = test.initStickyHeaderABTests(
|
|
||||||
abConfig,
|
abConfig,
|
||||||
// isStickyHeaderFeatureAllowed
|
|
||||||
isEnabled,
|
isEnabled,
|
||||||
( experiment ) => ( {
|
isUserInTreatmentBucket,
|
||||||
name: experiment.name,
|
expectedResult
|
||||||
isInBucket: () => true,
|
} ) => {
|
||||||
isInSample: () => true,
|
document.documentElement.classList.add( 'vector-sticky-header-enabled' );
|
||||||
getBucket: () => 'bucket',
|
const result = test.initStickyHeaderABTests(
|
||||||
isInTreatmentBucket: () => {
|
abConfig,
|
||||||
return isUserInTreatmentBucket;
|
// isStickyHeaderFeatureAllowed
|
||||||
}
|
isEnabled,
|
||||||
} )
|
( experiment ) => ( {
|
||||||
);
|
name: experiment.name,
|
||||||
expect( result ).toMatchObject( expectedResult );
|
isInBucket: () => true,
|
||||||
// Check that there are no side effects
|
isInSample: () => true,
|
||||||
expect(
|
getBucket: () => 'bucket',
|
||||||
document.documentElement.classList.contains( 'vector-sticky-header-enabled' )
|
isInTreatmentBucket: () => {
|
||||||
).toBe( true );
|
return isUserInTreatmentBucket;
|
||||||
} );
|
}
|
||||||
|
} )
|
||||||
|
);
|
||||||
|
expect( result ).toMatchObject( expectedResult );
|
||||||
|
// Check that there are no side effects
|
||||||
|
expect(
|
||||||
|
document.documentElement.classList.contains( 'vector-sticky-header-enabled' )
|
||||||
|
).toBe( true );
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
Loading…
Reference in New Issue