resourceloader: Add skin-based 'mediawiki.skin.variables.less' import
Add the SkinLessImportPaths attribute for skin-specific LESS import paths, which skins can use to override the mediawiki.skin.variables.less file. As a starting point, add the following 5 variables: * device widths (3x) To help phase out 'mediawiki.ui/variables'. These are commonly used by MobileFrontend. * @font-family-sans Recommended by Volker. Used by multiple skins. * @border-radius-base Recommended by Volker as example of something that we currently hardcode in MediaWiki core for Vector and OOUI/WikimediaUI in 'mediawiki.widgets.datetime' but should instead be allowed to vary by skin and OOUI theme. Remove the hardcoded value for '@border-radius-base' in various places in favour of importing from mediawiki.skin. The default is a bare default of 0 (as border-radius is off by default in the browser). The value for Vector is restored there by I47da304667811. The value for MonoBook is improved by I000f319ab31b. Bug: T112747 Change-Id: Icf86c930a3b5524254bb549624737d3b9dccb032
This commit is contained in:
parent
4d8cb4dc16
commit
0c01d8cc52
17 changed files with 163 additions and 33 deletions
|
|
@ -190,6 +190,10 @@ because of Phabricator reports.
|
|||
existing code, but it only supports version 2 of the test file
|
||||
specification and may be more strict when parsing invalid input,
|
||||
including duplicate tests.
|
||||
* The SkinLessImportPaths attribute was added, allowing skins to add a
|
||||
directory to the import path for LESS stylesheets. Skins can use this
|
||||
to provide a custom version of mediawiki.skin.variables.less, setting
|
||||
skin-specific values for certain LESS variables.
|
||||
* …
|
||||
|
||||
== Compatibility ==
|
||||
|
|
|
|||
|
|
@ -420,6 +420,10 @@
|
|||
"type": "object",
|
||||
"description": "ResourceLoader sources to register"
|
||||
},
|
||||
"SkinLessImportPaths": {
|
||||
"type": "object",
|
||||
"description": "Path to the skin-specific LESS import directory, keyed by skin name. Can be used to define skin-specific LESS variables."
|
||||
},
|
||||
"QUnitTestModule": {
|
||||
"type": "object",
|
||||
"description": "A ResourceLoaderFileModule definition registered only when wgEnableJavaScriptTest is true.",
|
||||
|
|
|
|||
|
|
@ -440,6 +440,10 @@
|
|||
"type": "object",
|
||||
"description": "ResourceLoader sources to register"
|
||||
},
|
||||
"SkinLessImportPaths": {
|
||||
"type": "object",
|
||||
"description": "Path to the skin-specific LESS import directory, keyed by skin name. Can be used to define skin-specific LESS variables."
|
||||
},
|
||||
"QUnitTestModule": {
|
||||
"type": "object",
|
||||
"description": "A ResourceLoaderFileModule definition registered only when wgEnableJavaScriptTest is true.",
|
||||
|
|
|
|||
|
|
@ -200,6 +200,7 @@ class ExtensionProcessor implements Processor {
|
|||
$this->extractHooks( $info, $path );
|
||||
$this->extractExtensionMessagesFiles( $dir, $info );
|
||||
$this->extractMessagesDirs( $dir, $info );
|
||||
$this->extractSkinImportPaths( $dir, $info );
|
||||
$this->extractNamespaces( $info );
|
||||
$this->extractResourceLoaderModules( $dir, $info );
|
||||
if ( isset( $info['ServiceWiringFiles'] ) ) {
|
||||
|
|
@ -618,6 +619,18 @@ class ExtensionProcessor implements Processor {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $dir
|
||||
* @param array $info
|
||||
*/
|
||||
protected function extractSkinImportPaths( $dir, array $info ) {
|
||||
if ( isset( $info['SkinLessImportPaths'] ) ) {
|
||||
foreach ( $info['SkinLessImportPaths'] as $skin => $subpath ) {
|
||||
$this->attributes['SkinLessImportPaths'][$skin] = "$dir/$subpath";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array $info
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ class ExtensionRegistry {
|
|||
private const LAZY_LOADED_ATTRIBUTES = [
|
||||
'TrackingCategories',
|
||||
'QUnitTestModules',
|
||||
'SkinLessImportPaths',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1906,10 +1906,11 @@ MESSAGE;
|
|||
* @param array $vars Associative array of variables that should be used
|
||||
* for compilation. Since 1.32, this method no longer automatically includes
|
||||
* global LESS vars from ResourceLoader::getLessVars (T191937).
|
||||
* @param array $importDirs Additional directories to look in for @import (since 1.36)
|
||||
* @throws MWException
|
||||
* @return Less_Parser
|
||||
*/
|
||||
public function getLessCompiler( $vars = [] ) {
|
||||
public function getLessCompiler( array $vars = [], array $importDirs = [] ) {
|
||||
global $IP;
|
||||
// When called from the installer, it is possible that a required PHP extension
|
||||
// is missing (at least for now; see T49564). If this is the case, throw an
|
||||
|
|
@ -1918,11 +1919,12 @@ MESSAGE;
|
|||
throw new MWException( 'MediaWiki requires the less.php parser' );
|
||||
}
|
||||
|
||||
$importDirs[] = "$IP/resources/src/mediawiki.less";
|
||||
|
||||
$parser = new Less_Parser;
|
||||
$parser->ModifyVars( $vars );
|
||||
$parser->SetImportDirs( [
|
||||
"$IP/resources/src/mediawiki.less/" => '',
|
||||
] );
|
||||
// SetImportDirs expects an array like [ 'path1' => '', 'path2' => '' ]
|
||||
$parser->SetImportDirs( array_fill_keys( $importDirs, '' ) );
|
||||
$parser->SetOption( 'relativeUrls', false );
|
||||
|
||||
return $parser;
|
||||
|
|
|
|||
|
|
@ -1076,15 +1076,26 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
|
|||
$cache = ObjectCache::getLocalServerInstance( CACHE_ANYTHING );
|
||||
}
|
||||
|
||||
$skinName = $context->getSkin();
|
||||
$skinImportPaths = ExtensionRegistry::getInstance()->getAttribute( 'SkinLessImportPaths' );
|
||||
$importDirs = [];
|
||||
if ( isset( $skinImportPaths[ $skinName ] ) ) {
|
||||
$importDirs[] = $skinImportPaths[ $skinName ];
|
||||
}
|
||||
|
||||
$vars = $this->getLessVars( $context );
|
||||
// Construct a cache key from a hash of the LESS source, and a hash digest
|
||||
// of the LESS variables used for compilation.
|
||||
ksort( $vars );
|
||||
$compilerParams = [
|
||||
'vars' => $vars,
|
||||
'importDirs' => $importDirs,
|
||||
];
|
||||
$key = $cache->makeGlobalKey(
|
||||
'resourceloader-less',
|
||||
'v1',
|
||||
hash( 'md4', $style ),
|
||||
hash( 'md4', serialize( $vars ) )
|
||||
hash( 'md4', serialize( $compilerParams ) )
|
||||
);
|
||||
|
||||
// If we got a cached value, we have to validate it by getting a checksum of all the
|
||||
|
|
@ -1094,7 +1105,8 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
|
|||
!$data ||
|
||||
$data['hash'] !== FileContentsHasher::getFileContentsHash( $data['files'] )
|
||||
) {
|
||||
$compiler = $context->getResourceLoader()->getLessCompiler( $vars );
|
||||
$compiler = $context->getResourceLoader()->getLessCompiler( $vars, $importDirs );
|
||||
|
||||
$css = $compiler->parse( $style, $stylePath )->getCss();
|
||||
// T253055: store the implicit dependency paths in a form relative to any install
|
||||
// path so that multiple version of the application can share the cache for identical
|
||||
|
|
|
|||
46
resources/src/mediawiki.less/mediawiki.skin.defaults.less
Normal file
46
resources/src/mediawiki.less/mediawiki.skin.defaults.less
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// This file is the central place where we declare
|
||||
// which variables are part of the "mediawiki.skin.variables.less"
|
||||
// API that core and extensions can use in their style modules.
|
||||
//
|
||||
// The initial values are intended merely as fallback to allow
|
||||
// forward and backward compatibility to allow new variables
|
||||
// to be defined without breaking existing implementations by
|
||||
// skins.
|
||||
//
|
||||
// #### Instructions for skins
|
||||
//
|
||||
// In skin.json, add:
|
||||
// "SkinLessImportPaths": {
|
||||
// "skinname": "resources/mediawiki.less"
|
||||
// }
|
||||
//
|
||||
// Create a file called resources/mediawiki.less/mediawiki.skin.variables.less, which starts with:
|
||||
// @import 'mediawiki.skin.default.less';
|
||||
// followed by any overrides for these variables, e.g.:
|
||||
// @width-breakpoint-desktop: 1234px;
|
||||
|
||||
// Minimum available screen width at which a device can be considered a mobile device.
|
||||
//
|
||||
// Many older feature phones have screens smaller than this value.
|
||||
//
|
||||
// @since 1.36
|
||||
@width-breakpoint-mobile: 320px;
|
||||
|
||||
// Minimum available screen width at which a device can be considered a tablet.
|
||||
//
|
||||
// The number is currently based on the device width of a Samsung Galaxy S5 mini and
|
||||
// is low enough to cover iPad (768px).
|
||||
//
|
||||
// @since 1.36
|
||||
@width-breakpoint-tablet: 720px;
|
||||
|
||||
// Minimum available screen width at which a device can be considered a desktop.
|
||||
//
|
||||
// @since 1.36
|
||||
@width-breakpoint-desktop: 1000px;
|
||||
|
||||
// @since 1.36
|
||||
@font-family-sans: sans-serif;
|
||||
|
||||
// @since 1.36
|
||||
@border-radius-base: 0;
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
// This file is used as a fallback match for "mediawiki.skin.variables.less"
|
||||
// in the LESS import directories for skins that do not define
|
||||
// this file themselves.
|
||||
|
||||
@import 'mediawiki.skin.defaults.less';
|
||||
|
|
@ -1,24 +1,4 @@
|
|||
/**
|
||||
* Minimum available screen width at which a device can be considered a mobile device
|
||||
* Many older feature phones have screens smaller than this value.
|
||||
* Number is prone to change with new information.
|
||||
* @since 1.31
|
||||
*/
|
||||
@width-breakpoint-mobile: 320px;
|
||||
|
||||
/**
|
||||
* Minimum available screen width at which a device can be considered a tablet
|
||||
* The number is currently based on the device width of a Samsung Galaxy S5 mini and is low
|
||||
* enough to cover iPad (768px). Number is prone to change with new information.
|
||||
* @since 1.31
|
||||
*/
|
||||
@width-breakpoint-tablet: 720px;
|
||||
/**
|
||||
* Minimum available screen width at which a device can be considered a desktop
|
||||
* Number is prone to change with new information.
|
||||
* @since 1.31
|
||||
*/
|
||||
@width-breakpoint-desktop: 1000px;
|
||||
@import 'mediawiki.skin.variables.less';
|
||||
|
||||
// Colors for use in mediawiki.ui
|
||||
|
||||
|
|
@ -97,9 +77,6 @@
|
|||
// Equal to OOUI.
|
||||
@border-width-radio--checked: 6px;
|
||||
|
||||
// Border radius to be used to buttons and inputs
|
||||
@border-radius-base: 2px;
|
||||
|
||||
// Box shadows
|
||||
@box-shadow-base: inset 0 0 0 1px transparent;
|
||||
@box-shadow-base--focus: inset 0 0 0 1px @color-primary--focus;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
@import 'mediawiki.skin.variables.less';
|
||||
|
||||
/*!
|
||||
* OOUI definitions used by the existing CSS (will make it easier to put this
|
||||
* widget in OOUI once OOUI is capable of handling it)
|
||||
|
|
@ -70,8 +72,6 @@
|
|||
@border-color-input--hover: @border-color-base--active;
|
||||
@border-color-erroneous: @color-erroneous;
|
||||
|
||||
@border-radius-base: 2px;
|
||||
|
||||
@box-shadow-base--focus: inset 0 0 0 1px @color-progressive;
|
||||
@box-shadow-dialog: 0 2px 2px 0 rgba( 0, 0, 0, 0.25 );
|
||||
@box-shadow-widget: inset 0 0 0 1px transparent;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
* @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
|
||||
* @license The MIT License (MIT); see LICENSE.txt
|
||||
*/
|
||||
@import 'mediawiki.skin.variables.less';
|
||||
|
||||
// Variables taken from OOUI's WikimediaUI theme
|
||||
@ooui-font-size-browser: 16; // assumed browser default of `16px`
|
||||
|
|
@ -17,7 +18,7 @@
|
|||
@border-base: 1px solid #a2a9b1;
|
||||
@border-color-base--focus: #36c;
|
||||
@border-color-input--hover: #72777d;
|
||||
@border-radius-base: 2px;
|
||||
// @border-radius-base is set in mediawiki.skin.variables.less
|
||||
|
||||
@padding-input-text: @padding-vertical-base @padding-horizontal-input-text;
|
||||
@padding-horizontal-input-text: 8px;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
@import 'mediawiki.skin.defaults.less';
|
||||
|
||||
@border-radius-base: 42px;
|
||||
3
tests/phpunit/data/less/use-variables-default.css
Normal file
3
tests/phpunit/data/less/use-variables-default.css
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.unit-tests {
|
||||
border-radius: 0;
|
||||
}
|
||||
3
tests/phpunit/data/less/use-variables-test.css
Normal file
3
tests/phpunit/data/less/use-variables-test.css
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.unit-tests {
|
||||
border-radius: 42px;
|
||||
}
|
||||
5
tests/phpunit/data/less/use-variables.less
Normal file
5
tests/phpunit/data/less/use-variables.less
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
@import 'mediawiki.skin.variables.less';
|
||||
|
||||
.unit-tests {
|
||||
border-radius: @border-radius-base;
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ class ResourceLoaderTest extends ResourceLoaderTestCase {
|
|||
parent::setUp();
|
||||
|
||||
$this->setMwGlobals( [
|
||||
'wgSkinLessVariablesImportPaths' => [],
|
||||
'wgShowExceptionDetails' => true,
|
||||
] );
|
||||
}
|
||||
|
|
@ -284,6 +285,52 @@ class ResourceLoaderTest extends ResourceLoaderTestCase {
|
|||
$this->assertStringEqualsFile( "$basePath/module/styles.css", $css );
|
||||
}
|
||||
|
||||
public static function provideMediaWikiVariablesCases() {
|
||||
$basePath = __DIR__ . '/../../data/less';
|
||||
return [
|
||||
[
|
||||
'config' => [],
|
||||
'importPaths' => [],
|
||||
'skin' => 'fallback',
|
||||
'expected' => "$basePath/use-variables-default.css",
|
||||
],
|
||||
[
|
||||
'config' => [
|
||||
'wgValidSkinNames' => [
|
||||
// Required to make ResourceLoaderContext::getSkin work
|
||||
'example' => 'Example',
|
||||
],
|
||||
],
|
||||
'importPaths' => [
|
||||
'example' => "$basePath/testvariables/",
|
||||
],
|
||||
'skin' => 'example',
|
||||
'expected' => "$basePath/use-variables-test.css",
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideMediaWikiVariablesCases
|
||||
* @covers ResourceLoader::getLessCompiler
|
||||
* @covers ResourceLoaderFileModule::compileLessFile
|
||||
*/
|
||||
public function testMediawikiVariablesDefault( array $config, array $importPaths, $skin, $expectedFile ) {
|
||||
$this->setMwGlobals( $config );
|
||||
$reset = ExtensionRegistry::getInstance()->setAttributeForTest( 'SkinLessImportPaths', $importPaths );
|
||||
// Reset Skin::getSkinNames for ResourceLoaderContext
|
||||
MediaWiki\MediaWikiServices::getInstance()->resetServiceForTesting( 'SkinFactory' );
|
||||
|
||||
$context = $this->getResourceLoaderContext( [ 'skin' => $skin ] );
|
||||
$module = new ResourceLoaderFileModule( [
|
||||
'localBasePath' => __DIR__ . '/../../data/less',
|
||||
'styles' => [ 'use-variables.less' ],
|
||||
] );
|
||||
$module->setName( 'test.less' );
|
||||
$styles = $module->getStyles( $context );
|
||||
$this->assertStringEqualsFile( $expectedFile, $styles['all'] );
|
||||
}
|
||||
|
||||
public static function providePackedModules() {
|
||||
return [
|
||||
[
|
||||
|
|
|
|||
Loading…
Reference in a new issue