From 8d8e2d85e6b34322963345c3ea59f952a7947c82 Mon Sep 17 00:00:00 2001 From: jdlrobson Date: Mon, 8 Feb 2021 15:10:21 -0800 Subject: [PATCH] Language button is quiet with focus, active and hover states * Add mediawiki ui button styles to Vector and convert language button to a quiet button * Restore the arrow for language button with ULS * Vertically align button to first line of header * Add a storybook entry for LanguageButton Additional changes: * Fix issues revealed by storybook - menu dropdown should reset generic typography rule for `ul` tags * Allow quotes usage in storybook without disable rule Bug: T268241 Change-Id: I483350084fb46a51c50af6aab78c62db6d02df89 --- .storybook/common.less | 1 + .storybook/icons.less | 4 ++ dev-scripts/setup-storybook.sh | 2 +- includes/SkinVector.php | 3 +- .../skins.vector.styles/LanguageButton.less | 38 ++++++++-------- .../skins.vector.styles/MenuDropdown.less | 5 +++ .../skins.vector.styles/layout-default.less | 5 +++ skin.json | 1 + skinStyles/mediawiki.ui.icon.less | 2 +- stories/.eslintrc.json | 3 ++ stories/Footer.stories.data.js | 2 - stories/LanguageButton.stories.data.js | 31 +++++++++++++ stories/LanguageButton.stories.js | 44 +++++++++++++++++++ stories/Menu.stories.data.js | 3 -- stories/MenuPortal.stories.data.js | 13 +----- stories/skin.stories.data.js | 2 - stories/types.js | 1 + stories/utils.js | 12 +++-- 18 files changed, 127 insertions(+), 45 deletions(-) create mode 100644 stories/LanguageButton.stories.data.js create mode 100644 stories/LanguageButton.stories.js diff --git a/.storybook/common.less b/.storybook/common.less index 673dfef..337920f 100644 --- a/.storybook/common.less +++ b/.storybook/common.less @@ -1,6 +1,7 @@ @import '../variables.less'; @import 'integration.less'; @import 'icons.less'; +@import '../skinStyles/mediawiki.ui.icon.less'; body { font-family: @font-family-sans; diff --git a/.storybook/icons.less b/.storybook/icons.less index 4c8f73f..3e60d52 100644 --- a/.storybook/icons.less +++ b/.storybook/icons.less @@ -18,3 +18,7 @@ background-image: linear-gradient(transparent,transparent), url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2220%22 height=%2220%22 viewBox=%220 0 20 20%22%3E %3Ctitle%3E language %3C/title%3E %3Cpath d=%22M20 18h-1.44a.61.61 0 0 1-.4-.12.81.81 0 0 1-.23-.31L17 15h-5l-1 2.54a.77.77 0 0 1-.22.3.59.59 0 0 1-.4.14H9l4.55-11.47h1.89zm-3.53-4.31L14.89 9.5a11.62 11.62 0 0 1-.39-1.24q-.09.37-.19.69l-.19.56-1.58 4.19zm-6.3-1.58a13.43 13.43 0 0 1-2.91-1.41 11.46 11.46 0 0 0 2.81-5.37H12V4H7.31a4 4 0 0 0-.2-.56C6.87 2.79 6.6 2 6.6 2l-1.47.5s.4.89.6 1.5H0v1.33h2.15A11.23 11.23 0 0 0 5 10.7a17.19 17.19 0 0 1-5 2.1q.56.82.87 1.38a23.28 23.28 0 0 0 5.22-2.51 15.64 15.64 0 0 0 3.56 1.77zM3.63 5.33h4.91a8.11 8.11 0 0 1-2.45 4.45 9.11 9.11 0 0 1-2.46-4.45z%22/%3E %3C/svg%3E") !important; } + +.mw-ui-icon-wikimedia-language:before { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Ctitle%3Elanguage%3C/title%3E%3Cg fill='%2354595d'%3E%3Cpath d='M20 18h-1.44a.61.61 0 01-.4-.12.81.81 0 01-.23-.31L17 15h-5l-1 2.54a.77.77 0 01-.22.3.59.59 0 01-.4.14H9l4.55-11.47h1.89zm-3.53-4.31L14.89 9.5a11.62 11.62 0 01-.39-1.24q-.09.37-.19.69l-.19.56-1.58 4.19zm-6.3-1.58a13.43 13.43 0 01-2.91-1.41 11.46 11.46 0 002.81-5.37H12V4H7.31a4 4 0 00-.2-.56C6.87 2.79 6.6 2 6.6 2l-1.47.5s.4.89.6 1.5H0v1.33h2.15A11.23 11.23 0 005 10.7a17.19 17.19 0 01-5 2.1q.56.82.87 1.38a23.28 23.28 0 005.22-2.51 15.64 15.64 0 003.56 1.77zM3.63 5.33h4.91a8.11 8.11 0 01-2.45 4.45 9.11 9.11 0 01-2.46-4.45z'/%3E%3C/g%3E%3C/svg%3E"); +} diff --git a/dev-scripts/setup-storybook.sh b/dev-scripts/setup-storybook.sh index faebddb..682fd9a 100755 --- a/dev-scripts/setup-storybook.sh +++ b/dev-scripts/setup-storybook.sh @@ -9,7 +9,7 @@ rm -f .storybook/resolve-imports/mediawiki.skin.variables.less cp resources/mediawiki.less/mediawiki.skin.variables.less .storybook/resolve-imports/ # Fetch resources via curl, `-sSL` silently, Show only errors, Location header and also with a 3XX response code. -curl -sS "https://en.wikipedia.org/w/load.php?only=styles&skin=vector&debug=true&modules=ext.echo.styles.badge|ext.uls.pt|wikibase.client.init|mediawiki.skinning.interface" -o .storybook/integration.less +curl -sS "https://en.wikipedia.org/w/load.php?only=styles&skin=vector&debug=true&modules=ext.echo.styles.badge|ext.uls.pt|wikibase.client.init|mediawiki.skinning.interface|mediawiki.ui.icon|mediawiki.ui.button" -o .storybook/integration.less curl -sSL "https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/core/+/master/resources/src/mediawiki.less/mediawiki.skin.defaults.less?format=TEXT" | base64 --decode > .storybook/resolve-imports/mediawiki.skin.defaults.less curl -sSL "https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/core/+/master/resources/src/mediawiki.less/mediawiki.mixins.less?format=TEXT" | base64 --decode > .storybook/resolve-imports/mediawiki.mixins.less curl -sSL "https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/core/+/master/resources/src/mediawiki.less/mediawiki.ui/variables.less?format=TEXT" | base64 --decode > .storybook/resolve-imports/mediawiki.ui/variables.less diff --git a/includes/SkinVector.php b/includes/SkinVector.php index 14463ea..154b3bc 100644 --- a/includes/SkinVector.php +++ b/includes/SkinVector.php @@ -252,7 +252,8 @@ class SkinVector extends SkinMustache { count( $this->getLanguages() ) )->parse(); // Adds language icon - $portletData['heading-class'] .= ' mw-ui-icon mw-ui-icon-before mw-ui-icon-wikimedia-language'; + $portletData['heading-class'] .= ' mw-ui-icon mw-ui-icon-before ' + . 'mw-ui-icon-wikimedia-language mw-ui-button mw-ui-quiet'; // Adds .mw-interlanguage-selector (ext.uls.interface attaches click // handler to this selector). $portletData['heading-class'] .= ' mw-interlanguage-selector'; diff --git a/resources/skins.vector.styles/LanguageButton.less b/resources/skins.vector.styles/LanguageButton.less index 46737a6..b0833db 100644 --- a/resources/skins.vector.styles/LanguageButton.less +++ b/resources/skins.vector.styles/LanguageButton.less @@ -1,27 +1,22 @@ -@import 'mediawiki.ui/mixins.buttons.less'; +// The use of mixins.buttons requires @font-size-base to be defined for this to work in Storybook +@import '../../variables.less'; // mw-body-header class can be removed when language button is the default. // e.g. upon removal of SkinVector::isLanguagesInHeader .mw-body-header .mw-portlet-lang { - .mw-ui-button-states(); - .vector-menu-heading { - color: inherit; - // In future this should use wvui - .mw-ui-button(); - padding-right: 30px; - // make sure label is center aligned. - display: flex; - align-items: center; + // Special treatment for language button, based on Vector font-size + font-size: @font-size-base; + // avoid sub pixel rendering + line-height: 1.493em; + // reset padding styles in MenuDropdown.less with right padding for arrow. + padding: 0 30px 0 8px; + // Prevent select of span text "X languages" + user-select: none; &:after { top: 0; } - - &:hover, - &:focus { - color: inherit; - } } .vector-menu-content { @@ -44,6 +39,14 @@ } } +// Disable border-radius when dropdown menu open +.client-nojs { + .mw-portlet-lang:hover .vector-menu-heading, + .vector-menu-checkbox:checked + .vector-menu-heading { + border-radius: 0; + } +} + // mw-body-header class can be removed when language button is the default. // e.g. upon removal of SkinVector::isLanguagesInHeader .client-js .mw-body-header { @@ -53,11 +56,6 @@ // Remove the ULS language icon provided by ext.uls.compactlinks.less as we // are already providing no-js users an icon. background-image: none; - - &:after { - // Don't show dropdown arrow. - content: none; - } } // When the ext.uls.interface module is loaded, we hide the fallback menu and diff --git a/resources/skins.vector.styles/MenuDropdown.less b/resources/skins.vector.styles/MenuDropdown.less index 26de6f4..3a4262b 100644 --- a/resources/skins.vector.styles/MenuDropdown.less +++ b/resources/skins.vector.styles/MenuDropdown.less @@ -72,6 +72,11 @@ visibility: visible; } + ul { + // display list style images introduced in typography.less. + list-style: none; + } + li { padding: 0; margin: 0; diff --git a/resources/skins.vector.styles/layout-default.less b/resources/skins.vector.styles/layout-default.less index d1dd5ae..c29ac0c 100644 --- a/resources/skins.vector.styles/layout-default.less +++ b/resources/skins.vector.styles/layout-default.less @@ -83,7 +83,12 @@ body { .mixin-clearfix(); #p-lang-btn { + @height-lang-button: 1.25em; float: right; + .box-sizing( border-box ); + height: @height-lang-button; + // should be vertically aligned. + margin-top: ( ( @font-size-heading-1 * @line-height-heading ) - @height-lang-button ) / 2; } } diff --git a/skin.json b/skin.json index b18c25b..dcff855 100644 --- a/skin.json +++ b/skin.json @@ -25,6 +25,7 @@ "skins.vector.js" ], "styles": [ + "mediawiki.ui.button", "skins.vector.styles", "skins.vector.icons", "mediawiki.ui.icon" diff --git a/skinStyles/mediawiki.ui.icon.less b/skinStyles/mediawiki.ui.icon.less index 41762ab..e5c78a4 100644 --- a/skinStyles/mediawiki.ui.icon.less +++ b/skinStyles/mediawiki.ui.icon.less @@ -1,6 +1,6 @@ // The .mw-page-container class is used to restrict this to the modern Vector. // This element is not needed in legacy Vector. -.mw-page-container .mw-ui-icon:before { +.mw-portlet-lang .mw-ui-icon:before { // mw-ui-icon in core defaults to 24x24. The style guide now requests 20x20. background-size: 20px auto; width: 20px; diff --git a/stories/.eslintrc.json b/stories/.eslintrc.json index 0d95fe6..024f2b5 100644 --- a/stories/.eslintrc.json +++ b/stories/.eslintrc.json @@ -7,6 +7,9 @@ "env": { "browser": true }, + "rules": { + "quotes": "off" + }, "parserOptions": { "sourceType": "module" } diff --git a/stories/Footer.stories.data.js b/stories/Footer.stories.data.js index 01ea726..c877dce 100644 --- a/stories/Footer.stories.data.js +++ b/stories/Footer.stories.data.js @@ -1,5 +1,3 @@ -/* eslint-disable quotes */ - import { htmlUserLanguageAttributes } from './utils'; import footerTemplate from '!!raw-loader!../includes/templates/Footer.mustache'; import footerRowTemplate from '!!raw-loader!../includes/templates/Footer__row.mustache'; diff --git a/stories/LanguageButton.stories.data.js b/stories/LanguageButton.stories.data.js new file mode 100644 index 0000000..cfa8d9f --- /dev/null +++ b/stories/LanguageButton.stories.data.js @@ -0,0 +1,31 @@ +/** + * @external MenuDefinition + */ +import { placeholder, htmlUserLanguageAttributes, portletAfter } from './utils'; + +/** + * @type {MenuDefinition} + */ +export const languageData = { + id: 'p-lang-btn', + // both classes needed for this to render correctly + class: 'mw-portlet-lang vector-menu-dropdown', + // mw-interlanguage-selector must be present to operate in ULS mode. + // icon classes and button classes + 'heading-class': 'vector-menu-heading mw-interlanguage-selector ' + + 'mw-ui-icon mw-ui-icon-before mw-ui-icon-wikimedia-language ' + + 'mw-ui-button mw-ui-quiet', + 'html-tooltip': 'A message tooltip-p-lang must exist for this to appear', + label: '10 languages', + 'html-user-language-attributes': htmlUserLanguageAttributes, + 'html-items': ` + +`, + 'html-after-portal': portletAfter( + `Edit links +${placeholder( `

Further hook output possible (lang)

`, 60 )}` + ) +}; diff --git a/stories/LanguageButton.stories.js b/stories/LanguageButton.stories.js new file mode 100644 index 0000000..2939ba6 --- /dev/null +++ b/stories/LanguageButton.stories.js @@ -0,0 +1,44 @@ +import mustache from 'mustache'; +import '../resources/skins.vector.styles/LanguageButton.less'; +import { vectorMenuTemplate } from './MenuDropdown.stories.data'; +import { languageData } from './LanguageButton.stories.data'; + +export default { + title: 'LanguageButton' +}; + +// mw-page-container is needed to enable the 20x20 icon +// mw-body-header can be removed when VectorLanguageInHeader is true and +// old language inside portal in modern Vector is no longer supported. +const CONTAINER_CLASS_MODERN_VECTOR = 'mw-body-header mw-page-container'; + +/** + * It allows us to support old and new renderings. + * + * @param {string|HTMLElement} htmlOrElement + * @param {string} className of containing element + * @return {HTMLElement} + */ +const wrapLanguageButton = ( htmlOrElement, className ) => { + const node = document.createElement( 'div' ); + node.setAttribute( 'class', className ); + if ( typeof htmlOrElement === 'string' ) { + node.innerHTML = htmlOrElement; + } else { + node.appendChild( htmlOrElement ); + } + return node; +}; + +export const languageButton = () => mustache.render( vectorMenuTemplate, languageData ); + +export const languageButtonWhenULSEnabled = () => wrapLanguageButton( + wrapLanguageButton( + wrapLanguageButton( + mustache.render( vectorMenuTemplate, languageData ), + 'vector-menu--hide-dropdown' + ), + CONTAINER_CLASS_MODERN_VECTOR + ), + 'client-js' +); diff --git a/stories/Menu.stories.data.js b/stories/Menu.stories.data.js index da77ee3..c71192d 100644 --- a/stories/Menu.stories.data.js +++ b/stories/Menu.stories.data.js @@ -1,9 +1,6 @@ /** * @external MenuDefinition */ - -/* eslint-disable quotes */ - import menuTemplate from '!!raw-loader!../includes/templates/Menu.mustache'; import { htmlUserLanguageAttributes } from './utils'; diff --git a/stories/MenuPortal.stories.data.js b/stories/MenuPortal.stories.data.js index 68e6c55..ed5a093 100644 --- a/stories/MenuPortal.stories.data.js +++ b/stories/MenuPortal.stories.data.js @@ -1,14 +1,11 @@ /** * @external MenuDefinition */ - -/* eslint-disable quotes */ - import mustache from 'mustache'; import { vectorMenuTemplate as portalTemplate } from './MenuDropdown.stories.data'; import '../resources/skins.vector.styles/MenuPortal.less'; import '../.storybook/common.less'; -import { placeholder, htmlUserLanguageAttributes } from './utils'; +import { placeholder, htmlUserLanguageAttributes, portletAfter } from './utils'; /** * @param {MenuDefinition} data @@ -21,14 +18,6 @@ export const wrapPortlet = ( data ) => { return node; }; -/** - * @param {string} html - * @return {string} - */ -const portletAfter = ( html ) => { - return `
${html}
`; -}; - /** * @type {Object.} */ diff --git a/stories/skin.stories.data.js b/stories/skin.stories.data.js index 7a9767c..10c90d6 100644 --- a/stories/skin.stories.data.js +++ b/stories/skin.stories.data.js @@ -1,5 +1,3 @@ -/* eslint-disable quotes */ - /** * @external Indicator */ diff --git a/stories/types.js b/stories/types.js index 9bae848..220931a 100644 --- a/stories/types.js +++ b/stories/types.js @@ -54,6 +54,7 @@ * @property {string} id * @property {string} label * @property {string} html-items + * @property {string} [heading-class] * @property {string} [html-tooltip] * @property {string} [class] of menu * @property {string} [html-user-language-attributes] diff --git a/stories/utils.js b/stories/utils.js index 7bfa490..9b3783d 100644 --- a/stories/utils.js +++ b/stories/utils.js @@ -1,5 +1,3 @@ -/* eslint-disable quotes */ - /** * @param {string} msg * @param {number} [height=200] @@ -11,6 +9,14 @@ const placeholder = ( msg, height ) => { display: flex; background: #eee; align-items: center;justify-content: center;">${msg}`; }; +/** + * @param {string} html + * @return {string} + */ +const portletAfter = ( html ) => { + return `
${html}
`; +}; + const htmlUserLanguageAttributes = `dir="ltr" lang="en-GB"`; -export { placeholder, htmlUserLanguageAttributes }; +export { placeholder, htmlUserLanguageAttributes, portletAfter };