mirror of
https://gerrit.wikimedia.org/r/mediawiki/skins/Vector.git
synced 2024-11-11 16:59:09 +00:00
Disable night mode if gadget detected
While our implementation of night mode is in beta, we want to respect the existing night mode gadget and disable night mode in favor of the gadget, providing a notice with an option to disable the gadget and reload the page Additionally, raise the max bundle size to account for the additional code added Note: the tests still aren't exactly where I'd like them to be, but hopefully they raise confidence a little bit with reviewing this patch Additional changes: * Upgrade to latest version of TypeScript types and remove several @ts-ignore statements Bug: T365083 Change-Id: I9583ee7ebf8c810ddd504193d568034c954d28f2
This commit is contained in:
parent
5340f59b61
commit
4a0c2cb684
|
@ -28,7 +28,7 @@
|
|||
},
|
||||
{
|
||||
"resourceModule": "skins.vector.js",
|
||||
"maxSize": "16.4 kB"
|
||||
"maxSize": "17.0 kB"
|
||||
},
|
||||
{
|
||||
"resourceModule": "skins.vector.legacy.js",
|
||||
|
|
|
@ -88,5 +88,7 @@
|
|||
"vector-main-menu-unpinned-popup": "The main menu has moved here.",
|
||||
"vector-appearance-unpinned-popup": "The appearance menu has moved here.",
|
||||
"vector-2022-beta-preview-label": "Accessibility for Reading (Vector 2022)",
|
||||
"vector-2022-beta-preview-description": "Get early access to the new reading accessibility features, such as typography improvements and dark mode."
|
||||
"vector-2022-beta-preview-description": "Get early access to the new reading accessibility features, such as typography improvements and dark mode.",
|
||||
"vector-night-mode-gadget-names": "dark-mode|dark-mode-toggle|dark-mode-toggle-pagestyles",
|
||||
"vector-night-mode-gadget-warning": "You're using a dark mode gadget that interferes with this feature. [[Special:Preferences#mw-prefsection-gadgets|Disable the gadget]] to use dark mode."
|
||||
}
|
||||
|
|
|
@ -105,5 +105,7 @@
|
|||
"vector-main-menu-unpinned-popup": "Text to show in popup body when Main menu ({{msg-mw|vector-main-menu-label}}) has been moved.",
|
||||
"vector-appearance-unpinned-popup": "Text to show in popup body when Appearance menu ({{msg-mw|vector-appearance-label}}) has been moved.",
|
||||
"vector-2022-beta-preview-label": "label for beta feature preview link in beta features under special preferences",
|
||||
"vector-2022-beta-preview-description": "label for beta feature preview description in beta features under special preferences"
|
||||
"vector-2022-beta-preview-description": "label for beta feature preview description in beta features under special preferences",
|
||||
"vector-night-mode-gadget-names": "pipe-separated list of gadget names associated with the night mode gadget",
|
||||
"vector-night-mode-gadget-warning": "a notice that night mode has been disabled due to a conflicting gadget, with a link to the gadgets page to disable"
|
||||
}
|
||||
|
|
14
package-lock.json
generated
14
package-lock.json
generated
|
@ -15,7 +15,7 @@
|
|||
"@wikimedia/codex": "1.6.0",
|
||||
"@wikimedia/codex-icons": "1.6.0",
|
||||
"@wikimedia/mw-node-qunit": "7.2.0",
|
||||
"@wikimedia/types-wikimedia": "0.4.3",
|
||||
"@wikimedia/types-wikimedia": "0.4.4",
|
||||
"eslint-config-wikimedia": "0.27.0",
|
||||
"eslint-plugin-no-jquery": "2.7.0",
|
||||
"grunt-banana-checker": "0.13.0",
|
||||
|
@ -2792,9 +2792,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/@wikimedia/types-wikimedia": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@wikimedia/types-wikimedia/-/types-wikimedia-0.4.3.tgz",
|
||||
"integrity": "sha512-c9qY4NUNLsc5OHpFIPd2EMMtqqI5g5PYMSg/ivaDxbn4gJf+1xbFzEC1kQCraCoIWFu9kvXdsMx+ZfhRsSkUaA==",
|
||||
"version": "0.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@wikimedia/types-wikimedia/-/types-wikimedia-0.4.4.tgz",
|
||||
"integrity": "sha512-OQ5WZ02E1XKNNljhFMBfgYdsBmJGp7vCmZZR4ZofPOd4sobvfWCWpW87ezPSePwJkB5YScWLQWNSaAeVoiXW7A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/abab": {
|
||||
|
@ -15242,9 +15242,9 @@
|
|||
}
|
||||
},
|
||||
"@wikimedia/types-wikimedia": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@wikimedia/types-wikimedia/-/types-wikimedia-0.4.3.tgz",
|
||||
"integrity": "sha512-c9qY4NUNLsc5OHpFIPd2EMMtqqI5g5PYMSg/ivaDxbn4gJf+1xbFzEC1kQCraCoIWFu9kvXdsMx+ZfhRsSkUaA==",
|
||||
"version": "0.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@wikimedia/types-wikimedia/-/types-wikimedia-0.4.4.tgz",
|
||||
"integrity": "sha512-OQ5WZ02E1XKNNljhFMBfgYdsBmJGp7vCmZZR4ZofPOd4sobvfWCWpW87ezPSePwJkB5YScWLQWNSaAeVoiXW7A==",
|
||||
"dev": true
|
||||
},
|
||||
"abab": {
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
"@wikimedia/codex": "1.6.0",
|
||||
"@wikimedia/codex-icons": "1.6.0",
|
||||
"@wikimedia/mw-node-qunit": "7.2.0",
|
||||
"@wikimedia/types-wikimedia": "0.4.3",
|
||||
"@wikimedia/types-wikimedia": "0.4.4",
|
||||
"eslint-config-wikimedia": "0.27.0",
|
||||
"eslint-plugin-no-jquery": "2.7.0",
|
||||
"grunt-banana-checker": "0.13.0",
|
||||
|
|
95
resources/skins.vector.js/disableNightModeIfGadget.js
Normal file
95
resources/skins.vector.js/disableNightModeIfGadget.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
/**
|
||||
* T365083 - Disable night mode if night mode gadget is enabled
|
||||
*
|
||||
* While our implementation of night mode is still in beta, we want to respect the existing gadget
|
||||
* and disable our version to avoid a double invert - that said, we will still provide a prompt for
|
||||
* the user to disable the gadget so they can try our night mode
|
||||
*/
|
||||
|
||||
/**
|
||||
* Are any of the gadgets associated with the broader night mode gadget enabled?
|
||||
* Note: This is localized to the names of the gadget in our particular language
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
function isNightModeGadgetEnabled() {
|
||||
return mw.msg( 'vector-night-mode-gadget-names' ).split( '|' ).some( ( gadget ) => {
|
||||
const state = mw.loader.getState( `ext.gadget.${ gadget }` );
|
||||
|
||||
// the state is null if it's not installed or we're on the preference page, otherwise it's
|
||||
// registered if the user doesn't have it turned on - all other states we consider enabled
|
||||
return state !== null && state !== 'registered';
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually mark the page we're on as excluded
|
||||
*/
|
||||
function disableNightModeForGadget() {
|
||||
document.documentElement.classList.remove( 'skin-theme-clientpref-night' );
|
||||
document.documentElement.classList.remove( 'skin-theme-clientpref-os' );
|
||||
|
||||
document.documentElement.classList.add( 'skin-theme-clientpref--excluded' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the link to disable the gadget so that, when clicked, it will disable the night mode
|
||||
* gadget rather than simply take you to the page
|
||||
* Note: The gadget names are similarly localized to the current language
|
||||
*
|
||||
* @param {Element} container an html element containing a link
|
||||
*/
|
||||
function alterDisableLink( container ) {
|
||||
const link = container.querySelector( 'a' );
|
||||
|
||||
// if we can't find a link, nothing we can do
|
||||
if ( !link ) {
|
||||
return;
|
||||
}
|
||||
|
||||
link.removeAttribute( 'title' );
|
||||
link.removeAttribute( 'href' );
|
||||
link.style.display = 'inline';
|
||||
|
||||
link.addEventListener( 'click', () => {
|
||||
const disableOptions = Object();
|
||||
|
||||
mw.msg( 'vector-night-mode-gadget-names' ).split( '|' ).forEach( ( gadgetName ) => {
|
||||
disableOptions[ `gadget-${ gadgetName }` ] = 0;
|
||||
} );
|
||||
|
||||
const api = new mw.Api();
|
||||
api.saveOptions( disableOptions ).then( () => {
|
||||
window.location.reload();
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the default exclusion message to indicate that we've disabled night mode on the page due
|
||||
* to a conflicting gadget, providing a link to disable the gadget in favor of our night mode
|
||||
*/
|
||||
function alterExclusionMessage() {
|
||||
const noticeContainer = document.querySelector( '.exclusion-notice' );
|
||||
|
||||
// if there's no exclusion notice, nothing we can do
|
||||
if ( !noticeContainer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
mw.loader.using( 'mediawiki.jqueryMsg' ).then( () => {
|
||||
// remove existing message
|
||||
noticeContainer.textContent = '';
|
||||
|
||||
mw.message( 'vector-night-mode-gadget-warning' ).parseDom().appendTo( noticeContainer );
|
||||
|
||||
alterDisableLink( noticeContainer );
|
||||
} );
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isNightModeGadgetEnabled,
|
||||
disableNightModeForGadget,
|
||||
alterDisableLink,
|
||||
alterExclusionMessage
|
||||
};
|
|
@ -16,7 +16,6 @@ function save( feature, enabled ) {
|
|||
case 'limited-width':
|
||||
case 'appearance-pinned':
|
||||
// Save the setting under the new system
|
||||
// @ts-ignore https://github.com/wikimedia/typescript-types/pull/44
|
||||
mw.user.clientPrefs.set( `vector-feature-${ feature }`, enabled ? '1' : '0' );
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -68,7 +68,6 @@ function hide( popupWidget ) {
|
|||
*/
|
||||
function show( popupWidget, timeout = 4000 ) {
|
||||
popupWidget.toggle( true );
|
||||
// @ts-ignore https://github.com/wikimedia/typescript-types/pull/40
|
||||
popupWidget.toggleClipping( true );
|
||||
// hide the popup after timeout ms
|
||||
if ( timeout === false ) {
|
||||
|
|
|
@ -13,6 +13,7 @@ const languageButton = require( './languageButton.js' ),
|
|||
setupIntersectionObservers = require( './setupIntersectionObservers.js' ),
|
||||
menuTabs = require( './menuTabs.js' ),
|
||||
legacyMessageBoxStyles = require( './legacyMessageBoxStyles.js' ),
|
||||
{ isNightModeGadgetEnabled, disableNightModeForGadget, alterExclusionMessage } = require( './disableNightModeIfGadget.js' ),
|
||||
teleportTarget = /** @type {HTMLElement} */require( /** @type {string} */ ( 'mediawiki.page.ready' ) ).teleportTarget;
|
||||
|
||||
/**
|
||||
|
@ -87,7 +88,16 @@ function main( window ) {
|
|||
// @ts-ignore issues relating to delete operator are not relevant here.
|
||||
delete clientPreferenceConfig[ 'skin-theme' ];
|
||||
}
|
||||
|
||||
// while we're in beta, temporarily check if the night mode gadget is installed and
|
||||
// disable our night mode if so
|
||||
if ( isNightModeGadgetEnabled() ) {
|
||||
disableNightModeForGadget();
|
||||
clientPreferences.render( appearanceMenuSelector, clientPreferenceConfig );
|
||||
alterExclusionMessage();
|
||||
} else {
|
||||
clientPreferences.render( appearanceMenuSelector, clientPreferenceConfig );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
|
|
10
skin.json
10
skin.json
|
@ -459,7 +459,8 @@
|
|||
"resources/skins.vector.js/languageButton.js",
|
||||
"resources/skins.vector.js/echo.js",
|
||||
"resources/skins.vector.js/searchLoader.js",
|
||||
"resources/skins.vector.js/menuTabs.js"
|
||||
"resources/skins.vector.js/menuTabs.js",
|
||||
"resources/skins.vector.js/disableNightModeIfGadget.js"
|
||||
],
|
||||
"dependencies": [
|
||||
"skins.vector.clientPreferences",
|
||||
|
@ -469,7 +470,8 @@
|
|||
"mediawiki.cookie",
|
||||
"mediawiki.experiments",
|
||||
"skins.vector.icons.js",
|
||||
"mediawiki.util"
|
||||
"mediawiki.util",
|
||||
"mediawiki.jqueryMsg"
|
||||
],
|
||||
"messages": [
|
||||
"vector-limited-width-toggle-on-popup",
|
||||
|
@ -483,7 +485,9 @@
|
|||
"vector-toc-unpinned-popup",
|
||||
"vector-page-tools-unpinned-popup",
|
||||
"vector-main-menu-unpinned-popup",
|
||||
"vector-appearance-unpinned-popup"
|
||||
"vector-appearance-unpinned-popup",
|
||||
"vector-night-mode-gadget-names",
|
||||
"vector-night-mode-gadget-warning"
|
||||
]
|
||||
},
|
||||
"skins.vector.legacy.js": {
|
||||
|
|
131
tests/jest/skins.vector.js/disableNightModeIfGadget.test.js
Normal file
131
tests/jest/skins.vector.js/disableNightModeIfGadget.test.js
Normal file
|
@ -0,0 +1,131 @@
|
|||
const {
|
||||
isNightModeGadgetEnabled,
|
||||
disableNightModeForGadget,
|
||||
alterDisableLink,
|
||||
alterExclusionMessage
|
||||
} = require( '../../../resources/skins.vector.js/disableNightModeIfGadget.js' );
|
||||
|
||||
describe( 'isNightModeGadgetEnabled', () => {
|
||||
beforeEach( () => {
|
||||
// https://github.com/wikimedia/mw-node-qunit/pull/38
|
||||
mw.loader.getState = () => null;
|
||||
} );
|
||||
|
||||
it( 'should return false if no gadgets are installed', () => {
|
||||
expect( isNightModeGadgetEnabled() ).toBeFalsy();
|
||||
} );
|
||||
|
||||
it( 'should return false if the gadgets are installed but not enabled', () => {
|
||||
// https://github.com/wikimedia/mw-node-qunit/pull/38
|
||||
mw.loader.getState = () => 'registered';
|
||||
|
||||
expect( isNightModeGadgetEnabled() ).toBeFalsy();
|
||||
} );
|
||||
|
||||
it( 'should return true if the gadgets are enabled', () => {
|
||||
// https://github.com/wikimedia/mw-node-qunit/pull/38
|
||||
mw.loader.getState = () => 'ready';
|
||||
|
||||
expect( isNightModeGadgetEnabled() ).toBeTruthy();
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'disableNightModeForGadget', () => {
|
||||
beforeEach( () => {
|
||||
document.documentElement.classList.remove( 'skin-theme-clientpref--excluded' );
|
||||
document.documentElement.classList.remove( 'skin-theme-clientpref-night' );
|
||||
document.documentElement.classList.remove( 'skin-theme-clientpref-os' );
|
||||
} );
|
||||
|
||||
it( 'should disable night mode', () => {
|
||||
document.documentElement.classList.add( 'skin-theme-clientpref-night' );
|
||||
|
||||
disableNightModeForGadget();
|
||||
|
||||
expect( document.documentElement.classList.contains( 'skin-theme-clientpref-night' ) ).toBeFalsy();
|
||||
} );
|
||||
|
||||
it( 'should disable automatic mode', () => {
|
||||
document.documentElement.classList.add( 'skin-theme-clientpref-os' );
|
||||
|
||||
disableNightModeForGadget();
|
||||
|
||||
expect( document.documentElement.classList.contains( 'skin-theme-clientpref-os' ) ).toBeFalsy();
|
||||
} );
|
||||
|
||||
it( 'should add the excluded class', () => {
|
||||
disableNightModeForGadget();
|
||||
|
||||
expect( document.documentElement.classList.contains( 'skin-theme-clientpref--excluded' ) ).toBeTruthy();
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'alterDisableLink', () => {
|
||||
it( 'should leave the surrounding element unaltered', () => {
|
||||
const p = document.createElement( 'p' );
|
||||
const a = document.createElement( 'a' );
|
||||
p.appendChild( a );
|
||||
|
||||
p.textContent = 'test';
|
||||
|
||||
alterDisableLink( p );
|
||||
|
||||
expect( p.textContent ).toBe( 'test' );
|
||||
} );
|
||||
|
||||
it( 'should strip the title and href attributes', () => {
|
||||
const p = document.createElement( 'p' );
|
||||
const a = document.createElement( 'a' );
|
||||
p.appendChild( a );
|
||||
|
||||
a.href = 'test.com';
|
||||
a.title = 'test';
|
||||
|
||||
alterDisableLink( p );
|
||||
|
||||
expect( a.href ).toBe( '' );
|
||||
expect( a.title ).toBe( '' );
|
||||
} );
|
||||
|
||||
it( 'should make the link display inline', () => {
|
||||
const p = document.createElement( 'p' );
|
||||
const a = document.createElement( 'a' );
|
||||
p.appendChild( a );
|
||||
|
||||
alterDisableLink( p );
|
||||
|
||||
expect( a.style.display ).toBe( 'inline' );
|
||||
} );
|
||||
|
||||
// actual click test to be added after https://github.com/wikimedia/mw-node-qunit/pull/39
|
||||
} );
|
||||
|
||||
describe( 'alterExclusionMessage', () => {
|
||||
beforeEach( () => {
|
||||
jest.spyOn( mw.loader, 'using' ).mockImplementation( () => ( {
|
||||
then: ( fn ) => fn()
|
||||
} ) );
|
||||
|
||||
// https://github.com/wikimedia/mw-node-qunit/pull/40
|
||||
jest.spyOn( mw, 'message' ).mockImplementation( () => ( {
|
||||
parseDom: () => ( {
|
||||
appendTo: () => {}
|
||||
} )
|
||||
} ) );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
jest.restoreAllMocks();
|
||||
} );
|
||||
|
||||
it( 'should remove the existing text from the notice', () => {
|
||||
const p = document.createElement( 'p' );
|
||||
document.documentElement.appendChild( p );
|
||||
p.className = 'exclusion-notice';
|
||||
p.textContent = 'test';
|
||||
|
||||
alterExclusionMessage();
|
||||
|
||||
expect( p.textContent ).toBe( '' );
|
||||
} );
|
||||
} );
|
Loading…
Reference in a new issue