[storybook] Document button usage and update broken stories

* add stories for buttons and icons using wvui and mwui
* Update user link stories
* Add the vector-menu-dropdown-noicon class to more and
variants menu
* Fixes the language button story and simplifies its code.
* Updates Hooks::makeButtonIcon to Hooks::makeIcon which
reflects the method purpose more. It doesn't output button
classes.

Bug: T289514
Change-Id: Ifd750da0c0181ec56f39ff00d653e88bfc848038
This commit is contained in:
jdlrobson 2021-09-01 15:58:54 -07:00 committed by Jdlrobson
parent df4717abec
commit 3ed167f318
13 changed files with 193 additions and 44 deletions

View File

@ -18,6 +18,22 @@
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%3Etray%3C/title%3E%3Cpath d=%22M3 1a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2zm14 12h-4l-1 2H8l-1-2H3V3h14z%22/%3E%3C/svg%3E");
}
.mw-ui-icon-add: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='M11 9V4H9v5H4v2h5v5h2v-5h5V9z'/%3E%3C/g%3E%3C/svg%3E");
}
.mw-ui-icon-add-invert: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='white'%3E%3Cpath d='M11 9V4H9v5H4v2h5v5h2v-5h5V9z'/%3E%3C/g%3E%3C/svg%3E");
}
.mw-ui-icon-add-progressive: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='%23447ff5'%3E%3Cpath d='M11 9V4H9v5H4v2h5v5h2v-5h5V9z'/%3E%3C/g%3E%3C/svg%3E");
}
.mw-ui-icon-add-destructive: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='%23ff4242'%3E%3Cpath d='M11 9V4H9v5H4v2h5v5h2v-5h5V9z'/%3E%3C/g%3E%3C/svg%3E");
}
.uls-trigger {
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;

View File

@ -227,7 +227,7 @@ class Hooks {
* @param string $name
* @return string of HTML
*/
public static function makeButtonIcon( $name ) {
public static function makeIcon( $name ) {
// Html::makeLink will pass this through rawElement
return '<span class="mw-ui-icon mw-ui-icon-' . $name . '"></span>';
}
@ -256,7 +256,7 @@ class Hooks {
if ( $hideText ) {
$item['link-class'][] = 'mw-ui-icon mw-ui-icon-element mw-ui-icon-' . $icon;
} else {
$item['link-html'] = self::makeButtonIcon( $icon );
$item['link-html'] = self::makeIcon( $icon );
}
}
$content_navigation[$menu][$key] = $item;

View File

@ -516,7 +516,7 @@ class SkinVector extends SkinMustache {
'label' => $label,
// ext.uls.interface attaches click handler to this selector.
'checkbox-class' => ' mw-interlanguage-selector ',
'html-vector-heading-icon' => Hooks::makeButtonIcon( 'wikimedia-language' ),
'html-vector-heading-icon' => Hooks::makeIcon( 'wikimedia-language' ),
'heading-class' =>
' vector-menu-heading ' .
' mw-ui-button mw-ui-quiet'

6
package-lock.json generated
View File

@ -2329,6 +2329,12 @@
"integrity": "sha512-H2MbvBt7DTROqm6kCRNdK6nUZuBvSYjq/7k01ZI5/uR9mj8FzxsMQcH4joh8kFVQ2LpDPXNsaPq/P5hqIDtSAw==",
"dev": true
},
"@wikimedia/wvui": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@wikimedia/wvui/-/wvui-0.3.0.tgz",
"integrity": "sha512-RiVvKReEn7KtvPzYghLr+e5FOYh71BWzaT1/uG/A2d8D6QM+0U5arF0MKJnB6drNvkQZPM/YcWu/IbV3zItcOA==",
"dev": true
},
"@xtuc/ieee754": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",

View File

@ -24,6 +24,7 @@
"@types/mustache": "4.0.1",
"@types/node-fetch": "2.5.7",
"@wikimedia/types-wikimedia": "0.2.0",
"@wikimedia/wvui": "0.3.0",
"babel-loader": "8.0.6",
"bundlesize": "0.18.1",
"eslint-config-wikimedia": "0.20.0",

View File

@ -0,0 +1,145 @@
import wvui from '@wikimedia/wvui';
import Vue from 'vue';
import '@wikimedia/wvui/dist/wvui.css';
const wvuiIconAdd = 'M11 9V4H9v5H4v2h5v5h2v-5h5V9z';
export default {
title: 'Icon and Buttons'
};
/**
*
* @typedef {Object} ButtonProps
* @property {string} type
* @property {string} action
*/
/**
* @param {ButtonProps} props
* @param {string} text
* @return {string}
*/
function makeButtonLegacy( props, text ) {
let typeClass = '';
let iconClass = 'mw-ui-icon-add';
switch ( props.type ) {
case 'quiet':
typeClass += ' mw-ui-quiet';
break;
}
switch ( props.action ) {
case 'progressive':
typeClass += ' mw-ui-progressive';
iconClass += '-progressive';
break;
case 'destructive':
typeClass += ' mw-ui-destructive';
iconClass += '-destructive';
break;
}
if ( props.type === 'primary' ) {
iconClass = 'mw-ui-icon-add-invert';
}
return `<button class="mw-ui-button ${typeClass}">
<span class="mw-ui-icon ${iconClass}"
></span>${text}
</button>`;
}
/**
* @param {ButtonProps} props
* @param {string} text
* @param {string} icon
* @return {string}
*/
function makeButton( props, text, icon ) {
const el = document.createElement( 'div' );
const vm = new Vue( {
el,
render: function ( createElement ) {
return createElement( wvui.WvuiButton, {
props
}, [
createElement( wvui.WvuiIcon, {
props: {
icon
}
} ),
text
] );
}
} );
return `
<tr>
<td>${makeButtonLegacy( props, text )}</td>
<td>${vm.$el.outerHTML}</td>
</tr>`;
}
/**
* @return {string}
*/
function makeIcon() {
return `
<tr>
<td>
<button class="mw-ui-icon mw-ui-icon-element mw-ui-icon-add
mw-ui-button">Normal Icon</button>
</td>
<td>N/A</td>
</tr>
<tr>
<td>
<button class="mw-ui-icon mw-ui-icon-element mw-ui-icon-add
mw-ui-button mw-ui-quiet">Quiet Icon</button>
</td>
<td>N/A</td>
</tr>`;
}
/**
*
* @param {string[]} btns
* @return {string}
*/
function makeButtons( btns ) {
return `<table>
<tbody>
<tr>
<th>Legacy</th>
<th>WVUI</th>
</tr>
${btns.join( '\n' )}
</tbody>
</table>`;
}
/**
* @return {string}
*/
export const Button = () => makeButtons( [
makeButton( {
action: 'default',
type: 'quiet'
}, 'Quiet button', wvuiIconAdd ),
makeButton( {
action: 'progressive',
type: 'quiet'
}, 'Quiet progressive', wvuiIconAdd ),
makeButton( {
action: 'destructive',
type: 'quiet'
}, 'Quiet destructive', wvuiIconAdd ),
makeButton( {
action: 'default',
type: 'normal'
}, 'Normal', wvuiIconAdd ),
makeButton( {
type: 'primary',
action: 'progressive'
}, 'Progressive primary', wvuiIconAdd ),
makeButton( {
type: 'primary',
action: 'destructive'
}, 'Destructive primary', wvuiIconAdd ),
makeIcon()
] );

View File

@ -1,7 +1,8 @@
/**
* @external MenuDefinition
*/
import { placeholder, htmlUserLanguageAttributes, portletAfter } from './utils';
import { placeholder, htmlUserLanguageAttributes,
makeIcon, portletAfter } from './utils';
/**
* @type {MenuDefinition}
@ -12,8 +13,9 @@ export const languageData = {
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 ' +
'checkbox-class': 'mw-interlanguage-selector',
'html-vector-heading-icon': makeIcon( 'wikimedia-language' ),
'heading-class': 'vector-menu-heading ' +
'mw-ui-button mw-ui-quiet',
'html-tooltip': 'A message tooltip-p-lang must exist for this to appear',
label: '10 languages',

View File

@ -7,38 +7,4 @@ 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'
);

View File

@ -12,7 +12,7 @@ export { vectorMenuTemplate };
*/
export const moreData = {
'is-dropdown': true,
class: 'vector-menu-dropdown',
class: 'vector-menu-dropdown vector-menu-dropdown-noicon',
label: 'More',
id: 'p-cactions',
'html-user-language-attributes': htmlUserLanguageAttributes,
@ -35,7 +35,7 @@ export const moreData = {
*/
export const variantsData = {
'is-dropdown': true,
class: 'vector-menu-dropdown',
class: 'vector-menu-dropdown vector-menu-dropdown-noicon',
label: '新加坡简体',
id: 'p-variants',
'html-user-language-attributes': htmlUserLanguageAttributes,

View File

@ -83,6 +83,7 @@ const additionalUserMoreData = {
const userMoreHtmlItems = ( isAnon = true ) => mustache.render( userLinksMoreTemplate, {
'is-anon': isAnon,
'is-create-account-allowed': isAnon,
'html-create-account': `<a href="/w/index.php?title=Special:CreateAccount&amp;returnto=Main+Page" class="mw-ui-button mw-ui-quiet" title="You are encouraged to create an account and log in; however, it is not mandatory">Create account</a>`,
'data-user-page': helperMakeMenuData( 'user-page', `
<li id="pt-userpage-2" class="user-links-collapsible-item">
@ -95,7 +96,7 @@ const userMoreHtmlItems = ( isAnon = true ) => mustache.render( userLinksMoreTem
const loggedInData = {
class: 'vector-user-menu vector-menu-dropdown vector-user-menu-logged-in',
'is-dropdown': true,
'heading-class': 'mw-ui-icon mw-ui-icon-element mw-ui-icon-wikimedia-userAvatar',
'heading-class': 'mw-ui-button mw-ui-quiet mw-ui-icon mw-ui-icon-element mw-ui-icon-wikimedia-userAvatar',
'is-anon': false,
'html-after-portal': mustache.render( userLinksLogoutTemplate, {
htmlLogout: `<a class="vector-menu-content-item vector-menu-content-item-logout mw-ui-icon mw-ui-icon-before mw-ui-icon-wikimedia-logOut" data-mw="interface" href="/w/index.php?title=Special:UserLogout&amp;returnto=Main+Page"><span>Log out</span></a>`
@ -105,7 +106,7 @@ const loggedInData = {
const loggedOutData = {
class: 'vector-user-menu vector-menu-dropdown vector-user-menu-logged-out',
'is-dropdown': true,
'heading-class': 'mw-ui-icon mw-ui-icon-element mw-ui-icon-wikimedia-ellipsis',
'heading-class': 'mw-ui-button mw-ui-quiet mw-ui-icon mw-ui-icon-element mw-ui-icon-wikimedia-ellipsis',
'is-anon': true,
'html-before-portal': mustache.render( userLinksLoginTemplate, {
htmlCreateAccount: `<a href="/w/index.php?title=Special:CreateAccount&amp;returnto=Special%3AUserLogout" icon="userAvatar" class="user-links-collapsible-item vector-menu-content-item mw-ui-icon mw-ui-icon-before mw-ui-icon-wikimedia-userAvatar" title="You are encouraged to create an account and log in; however, it is not mandatory"><span>Create account</span></a>`,

View File

@ -57,6 +57,8 @@
* @property {string} id
* @property {string} label
* @property {string} html-items
* @property {string} [checkbox-class]
* @property {string} [html-vector-heading-icon]
* @property {string} [heading-class]
* @property {string} [html-tooltip]
* @property {string} [class] of menu

View File

@ -2,6 +2,14 @@
* @external MenuDefinition
*/
/**
* @param {string} name
* @return {string}
*/
function makeIcon( name ) {
return `<span class="mw-ui-icon mw-ui-icon-${name}"></span>`;
}
/**
* @param {string} msg
* @param {number} [height=200]
@ -64,6 +72,7 @@ function helperMakeMenuData( name, htmlItems, additionalData = {} ) {
}
export {
makeIcon,
placeholder,
htmlUserLanguageAttributes,
portletAfter,

View File

@ -9,6 +9,7 @@
"noImplicitReturns": true,
"newLine": "lf",
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"pretty": true,
"target": "es5",
"lib": [ "dom" ],