diff --git a/.storybook/icons.less b/.storybook/icons.less index 4c1c710..9da1d46 100644 --- a/.storybook/icons.less +++ b/.storybook/icons.less @@ -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; diff --git a/includes/Hooks.php b/includes/Hooks.php index d14f168..870edd0 100644 --- a/includes/Hooks.php +++ b/includes/Hooks.php @@ -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 ''; } @@ -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; diff --git a/includes/SkinVector.php b/includes/SkinVector.php index 8f3c5e7..8a1c8d9 100644 --- a/includes/SkinVector.php +++ b/includes/SkinVector.php @@ -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' diff --git a/package-lock.json b/package-lock.json index bfd46b8..8f7ec84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 53f068c..49dcc76 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/stories/ButtonsAndIcons.stories.js b/stories/ButtonsAndIcons.stories.js new file mode 100644 index 0000000..114bbbf --- /dev/null +++ b/stories/ButtonsAndIcons.stories.js @@ -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 ``; +} + +/** + * @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 ` + + ${makeButtonLegacy( props, text )} + ${vm.$el.outerHTML} + `; +} + +/** + * @return {string} + */ +function makeIcon() { + return ` + + + + + N/A + + + + + + N/A + `; +} + +/** + * + * @param {string[]} btns + * @return {string} + */ +function makeButtons( btns ) { + return ` + + + + + + ${btns.join( '\n' )} + +
LegacyWVUI
`; +} + +/** + * @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() +] ); diff --git a/stories/LanguageButton.stories.data.js b/stories/LanguageButton.stories.data.js index cfa8d9f..f530da2 100644 --- a/stories/LanguageButton.stories.data.js +++ b/stories/LanguageButton.stories.data.js @@ -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', diff --git a/stories/LanguageButton.stories.js b/stories/LanguageButton.stories.js index 89e2a66..0d1ce38 100644 --- a/stories/LanguageButton.stories.js +++ b/stories/LanguageButton.stories.js @@ -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' -); diff --git a/stories/MenuDropdown.stories.data.js b/stories/MenuDropdown.stories.data.js index 123283d..35abb73 100644 --- a/stories/MenuDropdown.stories.data.js +++ b/stories/MenuDropdown.stories.data.js @@ -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, diff --git a/stories/UserLinks.stories.data.js b/stories/UserLinks.stories.data.js index d878f70..1d8a41f 100644 --- a/stories/UserLinks.stories.data.js +++ b/stories/UserLinks.stories.data.js @@ -83,6 +83,7 @@ const additionalUserMoreData = { const userMoreHtmlItems = ( isAnon = true ) => mustache.render( userLinksMoreTemplate, { 'is-anon': isAnon, + 'is-create-account-allowed': isAnon, 'html-create-account': `Create account`, 'data-user-page': helperMakeMenuData( 'user-page', `