Make the limited width and font size controls work for logged in users

For time being manage classes inside Vector. Document with FIXME and a
test.

Bug: T351447
Change-Id: Icc084a59a0141ec2b7c772cf92e8cc58c11f15b1
This commit is contained in:
Jon Robson 2023-11-20 14:55:41 -08:00 committed by Jdlrobson
parent c4802af02c
commit 845a0ab71d
7 changed files with 81 additions and 22 deletions

View file

@ -47,6 +47,10 @@ module.exports = {
'vue' 'vue'
], ],
modulePathIgnorePatterns: [
'<rootDir>/tests/integration-qunit/'
],
// The paths to modules that run some code to configure or // The paths to modules that run some code to configure or
// set up the testing environment before each test // set up the testing environment before each test
setupFiles: [ setupFiles: [

14
package-lock.json generated
View file

@ -15,7 +15,7 @@
"@wikimedia/codex": "0.20.0", "@wikimedia/codex": "0.20.0",
"@wikimedia/codex-icons": "0.20.0", "@wikimedia/codex-icons": "0.20.0",
"@wikimedia/mw-node-qunit": "7.2.0", "@wikimedia/mw-node-qunit": "7.2.0",
"@wikimedia/types-wikimedia": "0.4.1", "@wikimedia/types-wikimedia": "0.4.2",
"eslint-config-wikimedia": "0.25.1", "eslint-config-wikimedia": "0.25.1",
"eslint-plugin-no-jquery": "2.7.0", "eslint-plugin-no-jquery": "2.7.0",
"grunt-banana-checker": "0.11.0", "grunt-banana-checker": "0.11.0",
@ -2562,9 +2562,9 @@
"dev": true "dev": true
}, },
"node_modules/@wikimedia/types-wikimedia": { "node_modules/@wikimedia/types-wikimedia": {
"version": "0.4.1", "version": "0.4.2",
"resolved": "https://registry.npmjs.org/@wikimedia/types-wikimedia/-/types-wikimedia-0.4.1.tgz", "resolved": "https://registry.npmjs.org/@wikimedia/types-wikimedia/-/types-wikimedia-0.4.2.tgz",
"integrity": "sha512-RJUZXQNo+z7zBjcRSsVRR5GH2/R8/aSJc9Vo/ntnTzr8v1TjUlM3Dh4sP6XQODYKm/0HQn+hln3bWyeU6YTyRw==", "integrity": "sha512-MWZJE6JRUYRSuwajjiO4l7xz6530MUqdTOP0t0AteVm8Gqs+hUEcs5tTmAVJDg/ByvzyZ/M/KsW1UcuTRs0N8g==",
"dev": true "dev": true
}, },
"node_modules/abab": { "node_modules/abab": {
@ -14469,9 +14469,9 @@
} }
}, },
"@wikimedia/types-wikimedia": { "@wikimedia/types-wikimedia": {
"version": "0.4.1", "version": "0.4.2",
"resolved": "https://registry.npmjs.org/@wikimedia/types-wikimedia/-/types-wikimedia-0.4.1.tgz", "resolved": "https://registry.npmjs.org/@wikimedia/types-wikimedia/-/types-wikimedia-0.4.2.tgz",
"integrity": "sha512-RJUZXQNo+z7zBjcRSsVRR5GH2/R8/aSJc9Vo/ntnTzr8v1TjUlM3Dh4sP6XQODYKm/0HQn+hln3bWyeU6YTyRw==", "integrity": "sha512-MWZJE6JRUYRSuwajjiO4l7xz6530MUqdTOP0t0AteVm8Gqs+hUEcs5tTmAVJDg/ByvzyZ/M/KsW1UcuTRs0N8g==",
"dev": true "dev": true
}, },
"abab": { "abab": {

View file

@ -26,7 +26,7 @@
"@wikimedia/codex": "0.20.0", "@wikimedia/codex": "0.20.0",
"@wikimedia/codex-icons": "0.20.0", "@wikimedia/codex-icons": "0.20.0",
"@wikimedia/mw-node-qunit": "7.2.0", "@wikimedia/mw-node-qunit": "7.2.0",
"@wikimedia/types-wikimedia": "0.4.1", "@wikimedia/types-wikimedia": "0.4.2",
"eslint-config-wikimedia": "0.25.1", "eslint-config-wikimedia": "0.25.1",
"eslint-plugin-no-jquery": "2.7.0", "eslint-plugin-no-jquery": "2.7.0",
"grunt-banana-checker": "0.11.0", "grunt-banana-checker": "0.11.0",

View file

@ -21,14 +21,40 @@ function getClientPreferences() {
).map( ( className ) => className.split( '-clientpref-' )[ 0 ] ); ).map( ( className ) => className.split( '-clientpref-' )[ 0 ] );
} }
/**
* @param {string} featureName
* @param {string} value
*/
function toggleDocClassAndSave( featureName, value ) {
const pref = config[ featureName ];
if ( mw.user.isNamed() ) {
// FIXME: Ideally this would be done in mw.user.clientprefs API.
// mw.user.clientPrefs.get is marked as being only stable for anonymous and temporary users.
// So instead we have to keep track of all the different possible values and remove them
// before adding the new class.
config[ featureName ].options.forEach( ( possibleValue ) => {
document.documentElement.classList.remove( `${featureName}-clientpref-${possibleValue}` );
} );
document.documentElement.classList.add( `${featureName}-clientpref-${value}` );
// Ideally this should be taken care of via a single core helper function.
mw.util.debounce( function () {
api = api || new mw.Api();
api.saveOption( pref.preferenceKey, value );
}, 100 )();
// END FIXME.
} else {
// This case is much simpler - the API transparently takes care of classes as well as storage.
mw.user.clientPrefs.set( featureName, value );
}
}
/** /**
* @param {Element} parent * @param {Element} parent
* @param {string} featureName * @param {string} featureName
* @param {ClientPreference} pref
* @param {string} value * @param {string} value
* @param {string} currentValue * @param {string} currentValue
*/ */
function appendRadioToggle( parent, featureName, pref, value, currentValue ) { function appendRadioToggle( parent, featureName, value, currentValue ) {
const input = document.createElement( 'input' ); const input = document.createElement( 'input' );
const name = `vector-client-pref-${featureName}-group`; const name = `vector-client-pref-${featureName}-group`;
const id = `vector-client-pref-${featureName}-value-${value}`; const id = `vector-client-pref-${featureName}-value-${value}`;
@ -55,14 +81,7 @@ function appendRadioToggle( parent, featureName, pref, value, currentValue ) {
container.appendChild( label ); container.appendChild( label );
parent.appendChild( container ); parent.appendChild( container );
input.addEventListener( 'change', () => { input.addEventListener( 'change', () => {
// @ts-ignore https://github.com/wikimedia/typescript-types/pull/44 toggleDocClassAndSave( featureName, value );
mw.user.clientPrefs.set( featureName, value );
if ( mw.user.isNamed() ) {
mw.util.debounce( function () {
api = api || new mw.Api();
api.saveOption( pref.preferenceKey, value );
}, 100 )();
}
} ); } );
} }
@ -87,17 +106,16 @@ function makeClientPreferenceBinaryToggle( featureName ) {
if ( !pref ) { if ( !pref ) {
return null; return null;
} }
// @ts-ignore https://github.com/wikimedia/typescript-types/pull/44
const currentValue = mw.user.clientPrefs.get( featureName ); const currentValue = mw.user.clientPrefs.get( featureName );
// The client preference was invalid. This shouldn't happen unless a gadget // The client preference was invalid. This shouldn't happen unless a gadget
// or script has modified the documentElement. // or script has modified the documentElement.
if ( !currentValue ) { if ( typeof currentValue === 'boolean' ) {
return null; return null;
} }
const row = createRow( '' ); const row = createRow( '' );
const form = document.createElement( 'form' ); const form = document.createElement( 'form' );
pref.options.forEach( ( value ) => { pref.options.forEach( ( value ) => {
appendRadioToggle( form, featureName, pref, value, currentValue ); appendRadioToggle( form, featureName, value, currentValue );
} ); } );
row.appendChild( form ); row.appendChild( form );
return row; return row;
@ -194,5 +212,6 @@ function bind( clickSelector, renderSelector ) {
} }
module.exports = { module.exports = {
bind, bind,
toggleDocClassAndSave,
render render
}; };

View file

@ -549,6 +549,14 @@
"+ext.uls.interface": "skinStyles/ext.uls.interface.less" "+ext.uls.interface": "skinStyles/ext.uls.interface.less"
} }
}, },
"QUnitTestModule": {
"localBasePath": "",
"remoteExtPath": "Vector",
"dependencies": [ "skins.vector.clientPreferences" ],
"scripts": [
"tests/integration-qunit/integration.test.js"
]
},
"config": { "config": {
"VectorClientPreferences": { "VectorClientPreferences": {
"value": { "value": {

View file

@ -0,0 +1,27 @@
/* global QUnit */
const clientPreferences = require( 'skins.vector.clientPreferences' );
/*!
* Vector integration tests.
*
* This should only be used to test APIs that Vector depends on to work.
* For unit tests please see tests/jest.
*/
QUnit.module( 'Vector (integration)', function () {
QUnit.test( 'Client preferences: Behaves same for all users', function ( assert ) {
const sandbox = this.sandbox;
const helper = ( feature, isNamedReturnValue ) => {
document.documentElement.setAttribute( 'class', `${feature}-clientpref-0` );
const stub = sandbox.stub( mw.user, 'isNamed', () => isNamedReturnValue );
clientPreferences.toggleDocClassAndSave( feature, '1' );
stub.restore();
return document.documentElement.getAttribute( 'class' );
};
assert.strictEqual(
helper( 'vector-feature-limited-width', false ),
helper( 'vector-feature-limited-width', true ),
'The same classes are modified regardless of the user status.'
);
} );
} );

View file

@ -4,7 +4,8 @@
"jest.setup.js", "jest.setup.js",
"vendor", "vendor",
"coverage", "coverage",
"tests/jest" "tests/jest",
"tests/integration-qunit"
], ],
"compilerOptions": { "compilerOptions": {
"resolveJsonModule": true, "resolveJsonModule": true,