mirror of
https://gerrit.wikimedia.org/r/mediawiki/skins/Vector.git
synced 2024-09-24 10:49:25 +00:00
Limited width toggle
* Introduce a generalized feature toggle system that uses user preferences or localStorage for anons (right now the latter is out of scope but will be explored in a follow up) * Feature flagged to VisualEnhancementNext for now, given the dependency on icon size Bug: T319449 Change-Id: I7343a3f38b720411d5ef5f3414f25f475b0bb84a
This commit is contained in:
parent
685a3171bd
commit
0eb8811e7a
|
@ -29,10 +29,10 @@ module.exports = {
|
|||
// An object that configures minimum threshold enforcement for coverage results
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 25,
|
||||
functions: 29,
|
||||
lines: 32,
|
||||
statements: 32
|
||||
branches: 26,
|
||||
functions: 32,
|
||||
lines: 34,
|
||||
statements: 34
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -4,3 +4,4 @@ var mockMediaWiki = require( '@wikimedia/mw-node-qunit/src/mockMediaWiki.js' );
|
|||
global.mw = mockMediaWiki();
|
||||
global.$ = require('jquery');
|
||||
global.mw.util.showPortlet = function() {};
|
||||
global.mw.Api.prototype.saveOption = function() {};
|
||||
|
|
58
resources/skins.vector.js/features.js
Normal file
58
resources/skins.vector.js/features.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
/** @interface MwApi */
|
||||
|
||||
var /** @type {MwApi} */api,
|
||||
debounce = require( /** @type {string} */ ( 'mediawiki.util' ) ).debounce;
|
||||
|
||||
/**
|
||||
* Saves preference to user preferences and/or localStorage.
|
||||
*
|
||||
* @param {string} feature
|
||||
* @param {boolean} enabled
|
||||
*/
|
||||
function save( feature, enabled ) {
|
||||
var featuresJSON,
|
||||
// @ts-ignore
|
||||
features = mw.storage.get( 'VectorFeatures' ) || '{}';
|
||||
|
||||
try {
|
||||
featuresJSON = JSON.parse( features );
|
||||
} catch ( e ) {
|
||||
featuresJSON = {};
|
||||
}
|
||||
featuresJSON[ feature ] = enabled;
|
||||
// @ts-ignore
|
||||
mw.storage.set( 'VectorFeatures', JSON.stringify( featuresJSON ) );
|
||||
|
||||
if ( !mw.user.isAnon() ) {
|
||||
debounce( function () {
|
||||
api = api || new mw.Api();
|
||||
api.saveOption( 'vector-' + feature, enabled ? 1 : 0 );
|
||||
}, 500 )();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @throws {Error} if unknown feature toggled.
|
||||
*/
|
||||
function toggle( name ) {
|
||||
var featureClassEnabled = 'vector-feature-' + name + '-enabled',
|
||||
classList = document.body.classList,
|
||||
featureClassDisabled = 'vector-feature-' + name + '-disabled';
|
||||
|
||||
if ( classList.contains( featureClassDisabled ) ) {
|
||||
classList.remove( featureClassDisabled );
|
||||
classList.add( featureClassEnabled );
|
||||
save( name, true );
|
||||
} else if ( classList.contains( featureClassEnabled ) ) {
|
||||
classList.add( featureClassDisabled );
|
||||
classList.remove( featureClassEnabled );
|
||||
save( name, false );
|
||||
} else {
|
||||
throw new Error( 'Attempt to toggle unknown feature: ' + name );
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
toggle: toggle
|
||||
};
|
1
resources/skins.vector.js/images/fullscreen-close.svg
Normal file
1
resources/skins.vector.js/images/fullscreen-close.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg"><g fill="#000" fill-rule="evenodd"><path d="M11.5 8.5v-5H13V7h3.5v1.5h-5ZM11.5 11.5v5H13V13h3.5v-1.5h-5ZM8.5 8.5v-5H7V7H3.5v1.5h5ZM8.5 11.5v5H7V13H3.5v-1.5h5Z"/></g></svg>
|
After Width: | Height: | Size: 234 B |
1
resources/skins.vector.js/images/fullscreen.svg
Normal file
1
resources/skins.vector.js/images/fullscreen.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg"><g fill="#000" fill-rule="evenodd"><path d="M16.5 3.5v5H15V5h-3.5V3.5h5ZM16.5 16.5v-5H15V15h-3.5v1.5h5ZM3.5 3.5v5H5V5h3.5V3.5h-5ZM3.5 16.5v-5H5V15h3.5v1.5h-5Z"/></g></svg>
|
After Width: | Height: | Size: 234 B |
15
resources/skins.vector.js/limitedWidthToggle.js
Normal file
15
resources/skins.vector.js/limitedWidthToggle.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
var features = require( './features.js' );
|
||||
|
||||
/**
|
||||
* adds a toggle button
|
||||
*/
|
||||
function init() {
|
||||
var toggle = document.createElement( 'div' );
|
||||
toggle.classList.add( 'mw-ui-icon', 'mw-ui-icon-element', 'mw-ui-button', 'vector-limited-width-toggle' );
|
||||
document.body.appendChild( toggle );
|
||||
toggle.addEventListener( 'click', function () {
|
||||
features.toggle( 'limited-width' );
|
||||
} );
|
||||
}
|
||||
|
||||
module.exports = init;
|
25
resources/skins.vector.js/limitedWidthToggle.less
Normal file
25
resources/skins.vector.js/limitedWidthToggle.less
Normal file
|
@ -0,0 +1,25 @@
|
|||
.vector-limited-width-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// Note on certain pages the control will have no effect e.g. Special:RecentChanges
|
||||
// Defining this at 1600px was a product decision so do not change it
|
||||
// (more context at https://phabricator.wikimedia.org/T319449#8346630)
|
||||
@media ( min-width: 1600px ) {
|
||||
.vector-feature-visual-enhancement-next-enabled .vector-limited-width-toggle {
|
||||
display: block;
|
||||
position: fixed;
|
||||
bottom: 8px;
|
||||
right: 8px;
|
||||
|
||||
&:before {
|
||||
background-image: url( images/fullscreen.svg );
|
||||
}
|
||||
}
|
||||
|
||||
.vector-feature-visual-enhancement-next-enabled.vector-feature-limited-width-content-disable {
|
||||
.vector-limited-width-toggle:before {
|
||||
background-image: url( images/fullscreen-close.svg );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ var languageButton = require( './languageButton.js' ),
|
|||
initSearchLoader = require( './searchLoader.js' ).initSearchLoader,
|
||||
dropdownMenus = require( './dropdownMenus.js' ).dropdownMenus,
|
||||
sidebarPersistence = require( './sidebarPersistence.js' ),
|
||||
limitedWidthToggle = require( './limitedWidthToggle.js' ),
|
||||
watchstar = require( './watchstar.js' ),
|
||||
// @ts-ignore
|
||||
menuTabs = require( './menuTabs.js' ),
|
||||
|
@ -81,6 +82,7 @@ function main( window ) {
|
|||
// menuTabs should follow `dropdownMenus` as that can move menu items from a
|
||||
// tab menu to a dropdown.
|
||||
menuTabs();
|
||||
limitedWidthToggle();
|
||||
addNamespacesGadgetSupport();
|
||||
if ( document.body.classList.contains( 'vector-feature-visual-enhancement-next-enabled' ) ) {
|
||||
watchstar();
|
||||
|
|
|
@ -381,6 +381,7 @@
|
|||
"mediawiki.page.ready",
|
||||
"mediawiki.page.watch.ajax",
|
||||
"mediawiki.util",
|
||||
"mediawiki.storage",
|
||||
"mediawiki.experiments"
|
||||
],
|
||||
"messages": [
|
||||
|
@ -391,6 +392,9 @@
|
|||
]
|
||||
},
|
||||
"skins.vector.js": {
|
||||
"styles": [
|
||||
"resources/skins.vector.js/limitedWidthToggle.less"
|
||||
],
|
||||
"packageFiles": [
|
||||
"resources/skins.vector.js/skin.js",
|
||||
{
|
||||
|
@ -403,6 +407,8 @@
|
|||
"resources/skins.vector.js/sidebarPersistence.js",
|
||||
"resources/skins.vector.js/languageButton.js",
|
||||
"resources/skins.vector.js/echo.js",
|
||||
"resources/skins.vector.js/limitedWidthToggle.js",
|
||||
"resources/skins.vector.js/features.js",
|
||||
"resources/skins.vector.js/searchLoader.js",
|
||||
"resources/skins.vector.js/menuTabs.js"
|
||||
],
|
||||
|
|
3
tests/jest/__mocks__/mediawiki.util.js
Normal file
3
tests/jest/__mocks__/mediawiki.util.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
debounce: ( /** @type {Function} */fn ) => fn
|
||||
};
|
34
tests/jest/skins.vector.js/features.test.js
Normal file
34
tests/jest/skins.vector.js/features.test.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
const features = require( '../../../resources/skins.vector.js/features.js' );
|
||||
|
||||
describe( 'features', () => {
|
||||
beforeEach( () => {
|
||||
document.body.setAttribute( 'class', 'vector-feature-foo-disabled vector-feature-bar-enabled hello' );
|
||||
} );
|
||||
|
||||
test( 'toggle', () => {
|
||||
features.toggle( 'foo' );
|
||||
features.toggle( 'bar' );
|
||||
|
||||
expect(
|
||||
document.body.classList.contains( 'vector-feature-foo-enabled' )
|
||||
).toBe( true );
|
||||
expect(
|
||||
document.body.classList.contains( 'vector-feature-foo-disabled' )
|
||||
).toBe( false );
|
||||
expect(
|
||||
document.body.classList.contains( 'vector-feature-bar-disabled' )
|
||||
).toBe( true );
|
||||
expect(
|
||||
document.body.classList.contains( 'vector-feature-bar-enabled' )
|
||||
).toBe( false );
|
||||
expect(
|
||||
document.body.classList.contains( 'hello' )
|
||||
).toBe( true );
|
||||
} );
|
||||
|
||||
test( 'toggle unknown feature', () => {
|
||||
expect( () => {
|
||||
features.toggle( 'unknown' );
|
||||
} ).toThrow();
|
||||
} );
|
||||
} );
|
Loading…
Reference in a new issue