Update popups to use codex buttons over mediawiki.ui.buttons

- Provides missing accessible label for the settings button

Bug: T340256
Change-Id: I86972322ae34f1d1df8d79c66daa9e34091f9dd3
This commit is contained in:
bwang 2023-07-07 12:59:15 -05:00
parent 67a718a5be
commit 6b659443b5
17 changed files with 106 additions and 67 deletions

View file

@ -4,10 +4,6 @@ const webpack = require( "webpack" );
module.exports = {
stories: ['./stories/*.stories.js' ],
webpackFinal: async (config, { configType }) => {
config.module.rules.push({
test: /\.css$/,
use: ['style-loader', 'css-loader']
});
config.module.rules.push({
test: /\.less$/,
use: ['style-loader', 'css-loader', {

View file

@ -21,6 +21,7 @@ import { storiesOf } from '@storybook/html';
* Popups dependencies
*/
import { createPointerMasks } from '../../src/ui/renderer.js';
import '../../node_modules/@wikimedia/codex/dist/codex.style.css';
/**
* Popups helpers

View file

@ -25,7 +25,8 @@
"UserGetDefaultOptions": "PopupsHooks",
"MakeGlobalVariablesScript": "PopupsHooks",
"LocalUserCreated": "PopupsHooks",
"GetBetaFeaturePreferences": "PopupsHooks"
"GetBetaFeaturePreferences": "PopupsHooks",
"ResourceLoaderRegisterModules": "PopupsHooks"
},
"HookHandlers": {
"PopupsHooks": {
@ -229,12 +230,12 @@
"mediawiki.jqueryMsg",
"mediawiki.storage",
"mediawiki.Title",
"mediawiki.ui.button",
"mediawiki.ui.checkbox",
"mediawiki.ui.icon",
"mediawiki.Uri",
"mediawiki.user",
"mediawiki.util"
"mediawiki.util",
"codex-search-styles"
]
}
},

View file

@ -28,6 +28,8 @@ use MediaWiki\Hook\MakeGlobalVariablesScriptHook;
use MediaWiki\MediaWikiServices;
use MediaWiki\Preferences\Hook\GetPreferencesHook;
use MediaWiki\ResourceLoader\Hook\ResourceLoaderGetConfigVarsHook;
use MediaWiki\ResourceLoader\Hook\ResourceLoaderRegisterModulesHook;
use MediaWiki\ResourceLoader\ResourceLoader;
use MediaWiki\User\Hook\UserGetDefaultOptionsHook;
use MediaWiki\User\UserOptionsManager;
use OutputPage;
@ -43,6 +45,7 @@ class PopupsHooks implements
GetPreferencesHook,
BeforePageDisplayHook,
ResourceLoaderGetConfigVarsHook,
ResourceLoaderRegisterModulesHook,
MakeGlobalVariablesScriptHook,
UserGetDefaultOptionsHook,
LocalUserCreatedHook
@ -320,4 +323,25 @@ class PopupsHooks implements
}
}
/**
* ResourceLoaderRegisterModules hook handler.
*
* Provides support for MLEB where needed.
*
* @see https://www.mediawiki.org/wiki/Manual:Hooks/ResourceLoaderRegisterModules
*
* @param ResourceLoader $resourceLoader
*/
public function onResourceLoaderRegisterModules( ResourceLoader $resourceLoader ): void {
if ( !$resourceLoader->getModule( 'codex-search-styles' ) ) {
// We're running an older version of MediaWiki.
$resourceLoader->register( [
'codex-search-styles' => [
'localBasePath' => dirname( __DIR__ ),
'remoteExtPath' => 'UniversalLanguageSelector',
'styles' => 'resources/codex.mleb.css',
]
] );
}
}
}

28
package-lock.json generated
View file

@ -18,8 +18,8 @@
"@wdio/local-runner": "7.4.6",
"@wdio/mocha-framework": "7.30.2",
"@wdio/sync": "7.4.6",
"@wikimedia/codex": "0.11.0",
"@wikimedia/codex-icons": "0.11.0",
"@wikimedia/codex": "0.14.0",
"@wikimedia/codex-icons": "0.14.0",
"@wikimedia/mw-node-qunit": "7.0.0",
"babel-loader": "8.0.4",
"browserslist-config-wikimedia": "0.2.0",
@ -6550,9 +6550,9 @@
}
},
"node_modules/@wikimedia/codex": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@wikimedia/codex/-/codex-0.11.0.tgz",
"integrity": "sha512-DYI9ewtEqNykzYp1nMcyrtbKpGbVPTWkbxV9QJIDIDe2GayA8XbfK/7u0V5cnTU90iqCLwqsEnQZiXX4QDvsWg==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@wikimedia/codex/-/codex-0.14.0.tgz",
"integrity": "sha512-qx3ADc2I5yDg448WVZ6TodGdV2vCs4SLgH6KdDx0obAr+1vsmBUyBhaVenJOL2WrMDq1rAxV1gx7YrbgOoU/MQ==",
"dev": true,
"engines": {
"node": ">=16",
@ -6563,9 +6563,9 @@
}
},
"node_modules/@wikimedia/codex-icons": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@wikimedia/codex-icons/-/codex-icons-0.11.0.tgz",
"integrity": "sha512-aXpGI5UsWiWk1uag5I2+YlJinflmUVEyFoDH8IOt+Nko7t/z6EbJCHoMV482pVl9Ygbk2/ukHQ3LOFyS0Dv8xQ==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@wikimedia/codex-icons/-/codex-icons-0.14.0.tgz",
"integrity": "sha512-xbHV+5OMhIXrtfweeDAXO/HN69fn+bSUZtns6hbMgWsAK8t3hYyhQRzUeFJxrS0tApG4X9NQxIQ6mOmjZwz/rA==",
"dev": true,
"engines": {
"node": ">=16",
@ -32567,16 +32567,16 @@
}
},
"@wikimedia/codex": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@wikimedia/codex/-/codex-0.11.0.tgz",
"integrity": "sha512-DYI9ewtEqNykzYp1nMcyrtbKpGbVPTWkbxV9QJIDIDe2GayA8XbfK/7u0V5cnTU90iqCLwqsEnQZiXX4QDvsWg==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@wikimedia/codex/-/codex-0.14.0.tgz",
"integrity": "sha512-qx3ADc2I5yDg448WVZ6TodGdV2vCs4SLgH6KdDx0obAr+1vsmBUyBhaVenJOL2WrMDq1rAxV1gx7YrbgOoU/MQ==",
"dev": true,
"requires": {}
},
"@wikimedia/codex-icons": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@wikimedia/codex-icons/-/codex-icons-0.11.0.tgz",
"integrity": "sha512-aXpGI5UsWiWk1uag5I2+YlJinflmUVEyFoDH8IOt+Nko7t/z6EbJCHoMV482pVl9Ygbk2/ukHQ3LOFyS0Dv8xQ==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@wikimedia/codex-icons/-/codex-icons-0.14.0.tgz",
"integrity": "sha512-xbHV+5OMhIXrtfweeDAXO/HN69fn+bSUZtns6hbMgWsAK8t3hYyhQRzUeFJxrS0tApG4X9NQxIQ6mOmjZwz/rA==",
"dev": true
},
"@wikimedia/mw-node-qunit": {

View file

@ -39,8 +39,8 @@
"@wdio/local-runner": "7.4.6",
"@wdio/mocha-framework": "7.30.2",
"@wdio/sync": "7.4.6",
"@wikimedia/codex": "0.11.0",
"@wikimedia/codex-icons": "0.11.0",
"@wikimedia/codex": "0.14.0",
"@wikimedia/codex-icons": "0.14.0",
"@wikimedia/mw-node-qunit": "7.0.0",
"babel-loader": "8.0.4",
"browserslist-config-wikimedia": "0.2.0",

Binary file not shown.

Binary file not shown.

View file

@ -9,3 +9,15 @@
position: absolute;
top: -1000px;
}
.cdx-button.cdx-button--icon-only {
// Hide text in icon only buttons
span + span {
.mixin-screen-reader-text();
}
}
// FIXME: Remove after Iac69f7b14b732b0bbaa6ba265379ed4c4cdb3f7e is merged
.cdx-button--fake-button {
justify-content: center;
}

View file

@ -306,10 +306,10 @@ export function bindBehavior( preview, behavior ) {
preview.el.addEventListener( 'click', behavior.click );
const icon = preview.el.querySelector( '.mwe-popups-settings-icon' );
if ( icon ) {
icon.href = behavior.settingsUrl;
icon.addEventListener( 'click', ( event ) => {
const button = preview.el.querySelector( '.mwe-popups-settings-button' );
if ( button ) {
button.href = behavior.settingsUrl;
button.addEventListener( 'click', ( event ) => {
event.stopPropagation();
behavior.showSettings( event );

View file

@ -3,7 +3,7 @@
*/
import { renderPopup } from '../popup/popup';
import { createNodeFromTemplate } from '../templateUtil';
import { escapeHTML, createNodeFromTemplate } from '../templateUtil';
const defaultExtractWidth = 215;
const templateHTML = `
@ -11,8 +11,9 @@ const templateHTML = `
<a class="mwe-popups-discreet"></a>
<a class="mwe-popups-extract"></a>
<footer>
<a class="mwe-popups-settings-icon">
<span class="mw-ui-icon mw-ui-icon-element mw-ui-icon-small mw-ui-icon-settings"></span>
<a class="cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--weight-quiet cdx-button--icon-only mwe-popups-settings-button">
<span class="mw-ui-icon mw-ui-icon-small mw-ui-icon-settings"></span>
<span class="mwe-popups-settings-button-label"></span>
</a>
</footer>
</div>
@ -37,9 +38,14 @@ export function renderPagePreview(
extract.setAttribute( 'dir', model.languageDirection );
extract.setAttribute( 'lang', model.languageCode );
el.querySelector( '.mwe-popups-settings-icon' )
el.querySelector( '.mwe-popups-settings-button' )
.setAttribute( 'title', linkTitle );
// Set label on settings icon button
const labelText = escapeHTML( mw.msg( 'popups-settings-icon-gear-title' ) );
const label = el.querySelector( '.mwe-popups-settings-button-label' );
label.textContent = labelText;
if ( thumbnail ) {
el.querySelector( '.mwe-popups-discreet' ).appendChild( thumbnail.el );
} else {

View file

@ -62,25 +62,12 @@
}
}
.mwe-popups-settings-icon {
display: block;
.mwe-popups-settings-button {
float: right; // positions icon near bottom right corner
border-radius: @border-radius-base;
opacity: 0.67;
transition: background-color 100ms, opacity 100ms;
&:hover {
// TODO: Replace by `@background-color-button-quiet--hover`
// as soon as available in mediawiki.skin.variables.
background-color: rgba( 0, 24, 73, 0.027 );
}
&:active {
// TODO: Replace by `@background-color-button-quiet--active`
// as soon as available in mediawiki.skin.variables.
background-color: rgba( 0, 24, 73, 0.082 );
opacity: 1;
}
pointer-events: auto; // Overrides pointer-events: none on footer to ensure the button is interactive
// !important needed to override 'responsive' button styles on smaller viewports defined in Vector's Button.less
min-width: 32px !important; /* stylelint-disable-line declaration-no-important */
min-height: 32px !important; /* stylelint-disable-line declaration-no-important */
}
.mwe-popups-extract {
@ -409,7 +396,7 @@
/* @noflip */
right: 0;
.mwe-popups-settings-icon {
.mwe-popups-settings-button {
/* @noflip */
float: left;
}

View file

@ -106,12 +106,15 @@ export function renderReferencePreview(
// TODO: Remove when not in Beta any more
if ( !mw.config.get( 'wgPopupsReferencePreviewsBetaFeature' ) ) {
// TODO: Do not remove this but move it up into the templateHTML constant!
const settingsIconLink = document.createElement( 'a' );
settingsIconLink.classList.add( 'mwe-popups-settings-icon' );
const settingsIconLabel = document.createElement( 'span' );
settingsIconLabel.classList.add( 'mw-ui-icon', 'mw-ui-icon-element', 'mw-ui-icon-small', 'mw-ui-icon-settings' );
settingsIconLink.append( settingsIconLabel );
el.querySelector( '.mwe-popups-settings' ).appendChild( settingsIconLink );
const settingsButton = document.createElement( 'button' );
settingsButton.classList.add( 'cdx-button', 'cdx-button--fake-button', 'cdx-button--fake-button--enabled', 'cdx-button--weight-quiet', 'cdx-button--icon-only', 'mwe-popups-settings-button' );
const settingsIcon = document.createElement( 'span' );
settingsIcon.classList.add( 'mw-ui-icon', 'mw-ui-icon-small', 'mw-ui-icon-settings' );
const settingsButtonLabel = document.createElement( 'span' );
settingsButtonLabel.textContent = mw.msg( 'popups-settings-icon-gear-title' );
settingsButton.append( settingsIcon );
settingsButton.append( settingsButtonLabel );
el.querySelector( '.mwe-popups-settings' ).appendChild( settingsButton );
} else {
// Change the styling when there is no content in the footer (to prevent empty space)
el.querySelector( '.mwe-popups-container' ).classList.add( 'footer-empty' );

View file

@ -58,12 +58,15 @@ export function renderSettingsDialog( model ) {
<section id='mwe-popups-settings'>
<header>
<div>
<div class='mw-ui-icon mw-ui-icon-element mw-ui-icon-popups-close close'>${closeLabel}</div>
<button class='cdx-button cdx-button--weight-quiet cdx-button--icon-only'>
<span class='mw-ui-icon mw-ui-icon-popups-close close'></span>
<span>${closeLabel}</span>
</button>
</div>
<h1>${heading}</h1>
<div>
<button class='save mw-ui-button mw-ui-progressive'>${saveLabel}</button>
<button class='okay mw-ui-button mw-ui-progressive' style='display:none;'>${okLabel}</button>
<button class='save cdx-button cdx-button--weight-primary cdx-button--action-progressive'>${saveLabel}</button>
<button class='okay cdx-button cdx-button--weight-primary cdx-button--action-progressive' style='display:none;'>${okLabel}</button>
</div>
</header>
<main id='mwe-popups-settings-form'>
@ -83,7 +86,7 @@ export function renderSettingsDialog( model ) {
</form>
</main>
<div class='mwe-popups-settings-help' style='display:none;'>
<div class="mw-ui-icon mw-ui-icon-element mw-ui-icon-footer"></div>
<div class="mw-ui-icon mw-ui-icon-footer"></div>
<p>${helpText}</p>
</div>
</section>

View file

@ -17,7 +17,7 @@
position: relative;
display: table;
width: 100%;
padding: 5px 7px 5px 0;
padding: 5px 7px;
> div {
display: table-cell;

View file

@ -18,7 +18,7 @@ function createPagePreview( isTall, hasThumbnail, thumbnail ) {
el: $( '<div>' ).append(
hasThumbnail ? $( '<image>' ) : '',
$( '<a>' ).addClass( 'mwe-popups-extract' ).text( 'extract' ),
$( '<a>' ).addClass( 'mwe-popups-settings-icon' )
$( '<a>' ).addClass( 'mwe-popups-settings-button' )
)[ 0 ],
isTall,
hasThumbnail,
@ -166,7 +166,7 @@ QUnit.test( 'createPagePreview', ( assert ) => {
'Language direction is safely espaced'
);
assert.strictEqual(
$( preview.el ).find( '.mwe-popups-settings-icon' ).attr( 'title' ),
$( preview.el ).find( '.mwe-popups-settings-button' ).attr( 'title' ),
'<popups-settings-icon-gear-title>',
'Title attribute is correct.'
);
@ -458,7 +458,7 @@ QUnit.test( 'bindBehavior - settings link click', function ( assert ) {
behavior = createBehavior( this.sandbox );
renderer.bindBehavior( preview, behavior );
preview.el.querySelector( '.mwe-popups-settings-icon' ).dispatchEvent( new Event( 'click' ) );
preview.el.querySelector( '.mwe-popups-settings-button' ).dispatchEvent( new Event( 'click' ) );
assert.false( behavior.previewDwell.called, 'Preview dwell is NOT called.' );
assert.false(
@ -475,7 +475,7 @@ QUnit.test( 'bindBehavior - settings link URL', function ( assert ) {
renderer.bindBehavior( preview, behavior );
assert.strictEqual(
$( preview.el ).find( '.mwe-popups-settings-icon' ).attr( 'href' ),
$( preview.el ).find( '.mwe-popups-settings-button' ).attr( 'href' ),
behavior.settingsUrl,
'Settings link URL is correct.'
);

View file

@ -63,6 +63,12 @@ module.exports = ( env, argv ) => ( {
options: {
removeSVGTagAttrs: false // Keep width and height attributes.
}
}, {
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
} ]
},
optimization: {
@ -112,8 +118,8 @@ module.exports = ( env, argv ) => ( {
// Minified uncompressed size limits for chunks / assets and entrypoints. Keep these numbers
// up-to-date and rounded to the nearest 10th of a kibibyte so that code sizing costs are
// well understood. Related to bundlesize minified, gzipped compressed file size tests.
maxAssetSize: 45.8 * 1024,
maxEntrypointSize: 45.8 * 1024,
maxAssetSize: 46.4 * 1024,
maxEntrypointSize: 46.4 * 1024,
// The default filter excludes map files but we rename ours.
assetFilter: ( filename ) => !filename.endsWith( srcMapExt )