Use template partials rather than HTML strings

CHANGES to  index.mustache:
Changes can be understood by looking at the diff of
the file stories/skin.stories.js

The additional changes in stories folder export the data passed to
those templates. A new file is used as exporting any variable in a
file suffixed stories.js will be assumed to be a story entry.

The changes to index.mustache are as follows:
* html-footer is replaced with data-footer and Footer
component is rendered via template partial
* html-navigation is replaced with data-navigation and
Navigation component is rendered via template partial

CHANGES to Navigation.mustache:
Changes are best explained by looking at the diff to
stories/navigation.stories.js
and navigation.stories.data.js
* html-personal-menu is replaced with data-personal-menu

Bug: T245456
Change-Id: Ie96e92447a932b8a7f3844df277a1d31a2af423c
This commit is contained in:
jdlrobson 2020-02-17 11:49:43 -08:00
parent d6f9d0c1ff
commit cdd5ebd74d
19 changed files with 415 additions and 287 deletions

View File

@ -98,9 +98,12 @@ class VectorTemplate extends BaseTemplate {
// - Prefix "msg-" for interface messages.
// - Prefix "page-" for data relating to the current page (e.g. Title, WikiPage, or OutputPage).
// - Prefix "hook-" for any thing generated from a hook.
// It should be followed by the name of the hook in hyphenated lowercase.
// It should be followed by the name of the hook in hyphenated lowercase.
// - Prefix "html-" for raw HTML (in front of other keys excluding `array-`, if applicable).
// Should be avoided where possible.
// - Prefix "array-" for anything that is iterable (in front of other keys is applicable)
// - Prefix "data-" for an object of template data that can be passed directly
// to a template partial.
// - Conditional values are null if absent.
$params = [
'html-headelement' => $this->get( 'headelement', '' ),
@ -140,32 +143,28 @@ class VectorTemplate extends BaseTemplate {
'html-debuglog' => $this->get( 'debughtml', '' ),
// From BaseTemplate::getTrail (handles bottom JavaScript)
'html-printtail' => $this->getTrail() . '</body></html>',
'html-footer' => $tp->processTemplate( 'Footer', [
'data-footer' => [
'html-userlangattributes' => $this->get( 'userlangattributes', '' ),
'html-hook-vector-before-footer' => $htmlHookVectorBeforeFooter,
'array-footer-rows' => $this->getTemplateFooterRows(),
] ),
'html-navigation' => $tp->processTemplate( 'Navigation', [
],
'data-navigation' => [
'html-navigation-heading' => $this->getMsg( 'navigation-heading' ),
'html-personal-menu' => $tp->processTemplate( 'PersonalMenu', $this->buildPersonalProps() ),
'html-navigation-left-tabs' =>
$tp->processTemplate( 'VectorTabs', $this->buildNamespacesProps() )
. $tp->processTemplate( 'VectorMenu', $this->buildVariantsProps() ),
'html-navigation-right-tabs' =>
$tp->processTemplate( 'VectorTabs', $this->buildViewsProps() )
. $tp->processTemplate( 'VectorMenu', $this->buildActionsProps() )
. $tp->processTemplate( 'SearchBox', $this->buildSearchProps() ),
'html-sidebar' => $tp->processTemplate( 'Sidebar',
[
'data-personal-menu' => $this->buildPersonalProps(),
'data-namespace-tabs' => $this->buildNamespacesProps(),
'data-variants' => $this->buildVariantsProps(),
'data-page-actions' => $this->buildViewsProps(),
'data-page-actions-more' => $this->buildActionsProps(),
'data-search-box' => $this->buildSearchProps(),
'data-sidebar' => [
'html-logo-attributes' => Xml::expandAttributes(
Linker::tooltipAndAccesskeyAttribs( 'p-logo' ) + [
'class' => 'mw-wiki-logo',
'href' => Skin::makeMainPageUrl(),
]
),
] + $this->buildSidebarProps( $this->get( 'sidebar', [] ) )
),
] ),
)
] + $this->buildSidebarProps( $this->get( 'sidebar', [] ) ),
],
];
// Prepare and output the HTML response

View File

@ -9,13 +9,30 @@
<div id="mw-navigation">
<h2>{{{html-navigation-heading}}}</h2>
<div id="mw-head">
{{{html-personal-menu}}}
{{#data-personal-menu}}
{{>PersonalMenu}}
{{/data-personal-menu}}
<div id="left-navigation">
{{{html-navigation-left-tabs}}}
{{#data-namespace-tabs}}
{{>VectorTabs}}
{{/data-namespace-tabs}}
{{#data-variants}}
{{>VectorMenu}}
{{/data-variants}}
</div>
<div id="right-navigation">
{{{html-navigation-right-tabs}}}
{{#data-page-actions}}
{{>VectorTabs}}
{{/data-page-actions}}
{{#data-page-actions-more}}
{{>VectorMenu}}
{{/data-page-actions-more}}
{{#data-search-box}}
{{>SearchBox}}
{{/data-search-box}}
</div>
</div>
{{{html-sidebar}}}
{{#data-sidebar}}
{{>Sidebar}}
{{/data-sidebar}}
</div>

View File

@ -1,3 +1,31 @@
{{!
string html-headelement a string of attribute HTML that begins with `<html> and ends with '</head>'
and contains `meta` tags and ResourceLoader internals.
string|null html-sitenotice the contents of a banner defined in MediaWiki:Sitenotice.
Also used by CentralNotice to inject banners into Vector.
string html-indicators raw HTML containing wiki-defined badges such as "good article", "featured article".
An empty string if none are defined.
string page-langcode the content language of the article. Assumed to be escaped HTML./
string html-title
string html-prebodyhtml
bool page-isarticle
string msg-tagline
string html-subtitle
string html-undelete
string html-newtalk
string msg-jumptonavigation
string msg-jumptosearch
string html-bodycontent
string html-printfooter
string html-catlinks
string html-debuglog
string html-dataAfterContent
object data-navigation for navigation template partial. see Navigation.mustache for documentation.
object data-footer for footer template partial. see Footer.mustache for documentation.
string html-printtail HTML to render at the end of the page contained necessary script tags for ResourceLoader
terminated with `</body></html>`.
}}
{{{html-headelement}}}
<div id="mw-page-base" class="noprint"></div>
<div id="mw-head-base" class="noprint"></div>
@ -29,6 +57,10 @@
</div>
</div>
{{{html-dataAfterContent}}}
{{{html-navigation}}}
{{{html-footer}}}
{{#data-navigation}}
{{>Navigation}}
{{/data-navigation}}
{{#data-footer}}
{{>Footer}}
{{/data-footer}}
{{{html-printtail}}}

View File

@ -2,6 +2,7 @@
"rules": {
"one-var": "off",
"single-quote": "off",
"no-restricted-properties": "off",
"quotes": "off"
},
"parserOptions": {

View File

@ -0,0 +1,78 @@
import { htmluserlangattributes, placeholder } from './utils';
import footerTemplate from '!!raw-loader!../includes/templates/Footer.mustache';
const FOOTER_ROWS = [
{
id: 'footer-info',
'array-items': [
{
id: 'footer-info-lastmod',
html: 'This page was last modified on 10 January 2020, at 21:24.'
},
{
id: 'footer-info-copyright',
html: `This text is available under the <a href="https://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-ShareAlike Licence</a>;
additional terms may apply. See <a href="https://foundation.wikimedia.org/wiki/Special:MyLanguage/Terms_of_Use">Terms of Use</a> for details.`
}
]
},
{
id: 'footer-places',
'array-items': [
{
id: 'footer-places-privacy',
html: `<a href="https://foundation.wikimedia.org/wiki/Privacy_policy" class="extiw" title="wmf:Privacy policy">Privacy policy</a>`
},
{
id: 'footer-places-about',
html: `<a href="/wiki/Wikipedia:About" title="Wikipedia:About">About Wikipedia</a>`
},
{
id: 'footer-places-disclaimer',
html: `<a href="/wiki/Wikipedia:General_disclaimer" title="Wikipedia:General disclaimer">Disclaimers</a>`
},
{
id: 'footer-places-contact',
html: `<a href="//en.wikipedia.org/wiki/Wikipedia:Contact_us">Contact Wikipedia</a>`
},
{
id: 'footer-places-developers',
html: `<a href="https://www.mediawiki.org/wiki/Special:MyLanguage/How_to_contribute">Developers</a>`
},
{
id: 'footer-places-statslink',
html: `<a href="https://stats.wikimedia.org/v2/#/en.wikipedia.org">Statistics</a>`
},
{
id: 'footer-places-cookiestatement',
html: `<a href="https://foundation.wikimedia.org/wiki/Cookie_statement">Cookie statement</a>`
},
{
id: 'footer-places-mobileview',
html: `<a href="//en.m.wikipedia.org/w/index.php?title=Paris&amp;useskin=vector&amp;mobileaction=toggle_view_mobile" class="noprint stopMobileRedirectToggle">Mobile view</a>`
}
]
},
{
id: 'footer-icons',
'array-items': [
{
id: 'footer-copyrightico',
html: `<a href="https://wikimediafoundation.org/"><img src="https://wikipedia.org/static/images/wikimedia-button.png" srcset="https://wikipedia.org/static/images/wikimedia-button-1.5x.png 1.5x, https://wikipedia.org/static/images/wikimedia-button-2x.png 2x" width="88" height="31" alt="Wikimedia Foundation"/></a>`
},
{
id: 'footer-poweredbyico',
html: `<a href="https://www.mediawiki.org/"><img src="https://wikipedia.org/static/images/poweredby_mediawiki_88x31.png" alt="Powered by MediaWiki" srcset="https://wikipedia.org/static/images/poweredby_mediawiki_132x47.png 1.5x, https://wikipedia.org/static/images/poweredby_mediawiki_176x62.png 2x" width="88" height="31"/></a>`
}
]
}
];
export { footerTemplate };
export const FOOTER_TEMPLATE_DATA = {
'html-userlangattributes': htmluserlangattributes,
'html-hook-vector-before-footer': placeholder( 'output of VectorBeforeFooter hook (deprecated 1.35)', 20 ),
'array-footer-rows': FOOTER_ROWS
};

View File

@ -1,6 +1,5 @@
import mustache from 'mustache';
import footerTemplate from '!!raw-loader!../includes/templates/Footer.mustache';
import { htmluserlangattributes, placeholder } from './utils';
import { FOOTER_TEMPLATE_DATA, footerTemplate } from './footer.stories.data';
import '../resources/skins.vector.styles/footer.less';
import '../.storybook/common.less';
@ -8,76 +7,4 @@ export default {
title: 'Footer'
};
const FOOTER_ROWS = [
{
id: 'footer-info',
'array-items': [
{
id: 'footer-info-lastmod',
html: 'This page was last modified on 10 January 2020, at 21:24.'
},
{
id: 'footer-info-copyright',
html: `This text is available under the <a href="https://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-ShareAlike Licence</a>;
additional terms may apply. See <a href="https://foundation.wikimedia.org/wiki/Special:MyLanguage/Terms_of_Use">Terms of Use</a> for details.`
}
]
},
{
id: 'footer-places',
'array-items': [
{
id: 'footer-places-privacy',
html: `<a href="https://foundation.wikimedia.org/wiki/Privacy_policy" class="extiw" title="wmf:Privacy policy">Privacy policy</a>`
},
{
id: 'footer-places-about',
html: `<a href="/wiki/Wikipedia:About" title="Wikipedia:About">About Wikipedia</a>`
},
{
id: 'footer-places-disclaimer',
html: `<a href="/wiki/Wikipedia:General_disclaimer" title="Wikipedia:General disclaimer">Disclaimers</a>`
},
{
id: 'footer-places-contact',
html: `<a href="//en.wikipedia.org/wiki/Wikipedia:Contact_us">Contact Wikipedia</a>`
},
{
id: 'footer-places-developers',
html: `<a href="https://www.mediawiki.org/wiki/Special:MyLanguage/How_to_contribute">Developers</a>`
},
{
id: 'footer-places-statslink',
html: `<a href="https://stats.wikimedia.org/v2/#/en.wikipedia.org">Statistics</a>`
},
{
id: 'footer-places-cookiestatement',
html: `<a href="https://foundation.wikimedia.org/wiki/Cookie_statement">Cookie statement</a>`
},
{
id: 'footer-places-mobileview',
html: `<a href="//en.m.wikipedia.org/w/index.php?title=Paris&amp;useskin=vector&amp;mobileaction=toggle_view_mobile" class="noprint stopMobileRedirectToggle">Mobile view</a>`
}
]
},
{
id: 'footer-icons',
'array-items': [
{
id: 'footer-copyrightico',
html: `<a href="https://wikimediafoundation.org/"><img src="https://wikipedia.org/static/images/wikimedia-button.png" srcset="https://wikipedia.org/static/images/wikimedia-button-1.5x.png 1.5x, https://wikipedia.org/static/images/wikimedia-button-2x.png 2x" width="88" height="31" alt="Wikimedia Foundation"/></a>`
},
{
id: 'footer-poweredbyico',
html: `<a href="https://www.mediawiki.org/"><img src="https://wikipedia.org/static/images/poweredby_mediawiki_88x31.png" alt="Powered by MediaWiki" srcset="https://wikipedia.org/static/images/poweredby_mediawiki_132x47.png 1.5x, https://wikipedia.org/static/images/poweredby_mediawiki_176x62.png 2x" width="88" height="31"/></a>`
}
]
}
];
export const footer = () => mustache.render( footerTemplate, {
'html-userlangattributes': htmluserlangattributes,
'html-hook-vector-before-footer': placeholder( 'output of VectorBeforeFooter hook (deprecated 1.35)', 20 ),
'array-footer-rows': FOOTER_ROWS
} );
export const footer = () => mustache.render( footerTemplate, FOOTER_TEMPLATE_DATA );

View File

@ -0,0 +1,62 @@
import vectorMenuTemplate from '!!raw-loader!../includes/templates/VectorMenu.mustache';
import { htmluserlangattributes } from './utils';
export { vectorMenuTemplate };
export const moreData = {
'empty-portlet': '',
'msg-label': 'More',
'menu-id': 'p-cactions',
'menu-label-id': 'p-cactions-label',
'html-userlangattributes': htmluserlangattributes,
'html-items': `<li id="ca-delete">
<a href="/w/index.php?title=Main_Page&amp;action=delete"
title="Delete this page [⌃⌥d]" accesskey="d">Delete</a>
</li>
<li id="ca-move">
<a href="/w/index.php/Special:MovePage/Main_Page"
title="Move this page [⌃⌥m]" accesskey="m">Move</a>
</li>
<li id="ca-protect">
<a href="/w/index.php?title=Main_Page&amp;action=protect"
title="Protect this page [⌃⌥=]" accesskey="=">Protect</a>
</li>`
};
export const variantsData = {
'msg-label': '新加坡简体',
'menu-id': 'p-variants',
'menu-label-id': 'p-variants-label',
'html-userlangattributes': htmluserlangattributes,
'html-items': `<li id="ca-varlang-0">
<a href="/zh/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD"
hreflang="zh" lang="zh">不转换</a></li>
<li id="ca-varlang-1">
<a href="/zh-hans/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD"
hreflang="zh-Hans" lang="zh-Hans">简体</a>
</li>
<li id="ca-varlang-2">
<a href="/zh-hant/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD"
hreflang="zh-Hant" lang="zh-Hant">繁體</a>
</li>
<li id="ca-varlang-3">
<a href="/zh-cn/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD"
hreflang="zh-Hans-CN" lang="zh-Hans-CN">大陆简体</a>
</li>
<li id="ca-varlang-4">
<a href="/zh-hk/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD"
hreflang="zh-Hant-HK" lang="zh-Hant-HK">香港繁體</a>
</li>
<li id="ca-varlang-5">
<a href="/zh-mo/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD"
hreflang="zh-Hant-MO" lang="zh-Hant-MO">澳門繁體</a>
</li>
<li id="ca-varlang-7" class="selected">
<a href="/zh-sg/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD"
hreflang="zh-Hans-SG" lang="zh-Hans-SG">新加坡简体</a>
</li>
<li id="ca-varlang-8">
<a href="/zh-tw/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD"
hreflang="zh-Hant-TW" lang="zh-Hant-TW">臺灣正體</a>
</li>`
};

View File

@ -1,67 +1,12 @@
import mustache from 'mustache';
import vectorMenu from '!!raw-loader!../includes/templates/VectorMenu.mustache';
import '../resources/skins.vector.styles/navigation.less';
import '../.storybook/common.less';
import { htmluserlangattributes } from './utils';
import { vectorMenuTemplate, moreData, variantsData } from './menu.stories.data';
export default {
title: 'Menu'
};
export const more = () => mustache.render( vectorMenu, {
'empty-portlet': '',
'msg-label': 'More',
'menu-id': 'p-cactions',
'menu-label-id': 'p-cactions-label',
'html-userlangattributes': htmluserlangattributes,
'html-items': `<li id="ca-delete">
<a href="/w/index.php?title=Main_Page&amp;action=delete"
title="Delete this page [⌃⌥d]" accesskey="d">Delete</a>
</li>
<li id="ca-move">
<a href="/w/index.php/Special:MovePage/Main_Page"
title="Move this page [⌃⌥m]" accesskey="m">Move</a>
</li>
<li id="ca-protect">
<a href="/w/index.php?title=Main_Page&amp;action=protect"
title="Protect this page [⌃⌥=]" accesskey="=">Protect</a>
</li>`
} );
export const more = () => mustache.render( vectorMenuTemplate, moreData );
export const variants = () => mustache.render( vectorMenu, {
'msg-label': '新加坡简体',
'menu-id': 'p-variants',
'menu-label-id': 'p-variants-label',
'html-userlangattributes': htmluserlangattributes,
'html-items': `<li id="ca-varlang-0">
<a href="/zh/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD"
hreflang="zh" lang="zh">不转换</a></li>
<li id="ca-varlang-1">
<a href="/zh-hans/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD"
hreflang="zh-Hans" lang="zh-Hans">简体</a>
</li>
<li id="ca-varlang-2">
<a href="/zh-hant/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD"
hreflang="zh-Hant" lang="zh-Hant">繁體</a>
</li>
<li id="ca-varlang-3">
<a href="/zh-cn/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD"
hreflang="zh-Hans-CN" lang="zh-Hans-CN">大陆简体</a>
</li>
<li id="ca-varlang-4">
<a href="/zh-hk/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD"
hreflang="zh-Hant-HK" lang="zh-Hant-HK">香港繁體</a>
</li>
<li id="ca-varlang-5">
<a href="/zh-mo/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD"
hreflang="zh-Hant-MO" lang="zh-Hant-MO">澳門繁體</a>
</li>
<li id="ca-varlang-7" class="selected">
<a href="/zh-sg/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD"
hreflang="zh-Hans-SG" lang="zh-Hans-SG">新加坡简体</a>
</li>
<li id="ca-varlang-8">
<a href="/zh-tw/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD"
hreflang="zh-Hant-TW" lang="zh-Hant-TW">臺灣正體</a>
</li>`
} );
export const variants = () => mustache.render( vectorMenuTemplate, variantsData );

View File

@ -0,0 +1,39 @@
import navTemplate from '!!raw-loader!../includes/templates/Navigation.mustache';
import { PERSONAL_MENU_TEMPLATE_DATA, personalMenuTemplate } from './personalNavigation.stories.data';
import { pageActionsData, namespaceTabsData, vectorTabsTemplate } from './tabs.stories.data';
import { vectorMenuTemplate, moreData, variantsData } from './menu.stories.data';
import { searchBoxData, searchBoxTemplate } from './searchBox.stories.data';
import { SIDEBAR_DATA, SIDEBAR_TEMPLATE_PARTIALS, sidebarTemplate } from './sidebar.stories.data';
export const NAVIGATION_TEMPLATE_PARTIALS = Object.assign( {}, SIDEBAR_TEMPLATE_PARTIALS, {
SearchBox: searchBoxTemplate,
Sidebar: sidebarTemplate,
VectorTabs: vectorTabsTemplate,
VectorMenu: vectorMenuTemplate,
PersonalMenu: personalMenuTemplate
} );
export const NAVIGATION_TEMPLATE_DATA = {
loggedOutWithVariants: {
'data-personal-menu': PERSONAL_MENU_TEMPLATE_DATA.loggedOut,
'data-namespace-tabs': namespaceTabsData,
'data-page-actions': pageActionsData,
'data-variants': variantsData,
'data-search-box': searchBoxData,
'data-sidebar': SIDEBAR_DATA.withPortals,
'html-navigation-heading': 'Navigation menu',
'html-logo-attributes': `class="mw-wiki-logo" href="/wiki/Main_Page" title="Visit the main page"`
},
loggedInWithMoreActions: {
'data-personal-menu': PERSONAL_MENU_TEMPLATE_DATA.loggedInWithEcho,
'data-namespace-tabs': namespaceTabsData,
'data-page-actions': pageActionsData,
'data-page-actions-more': moreData,
'data-search-box': searchBoxData,
'data-sidebar': SIDEBAR_DATA.withPortals,
'html-navigation-heading': 'Navigation menu',
'html-logo-attributes': `class="mw-wiki-logo" href="/wiki/Main_Page" title="Visit the main page"`
}
};
export { navTemplate };

View File

@ -1,36 +1,19 @@
import mustache from 'mustache';
import navTemplate from '!!raw-loader!../includes/templates/Navigation.mustache';
import { navTemplate, NAVIGATION_TEMPLATE_DATA,
NAVIGATION_TEMPLATE_PARTIALS } from './navigation.stories.data';
import '../.storybook/common.less';
import '../resources/skins.vector.styles/navigation.less';
import { loggedOut, loggedInWithEcho } from './personalNavigation.stories';
import { viewTabs, namespaceTabs } from './tabs.stories';
import { more, variants } from './menu.stories';
import { simpleSearch } from './searchBox.stories';
import { sidebarWithPortals } from './sidebar.stories';
export default {
title: 'Navigation (Header + Sidebar)'
};
export const navigationLoggedOutWithVariants = () => mustache.render( navTemplate,
{
'html-personalmenu': loggedOut(),
'html-navigation-left-tabs': namespaceTabs() + variants(),
'html-navigation-right-tabs': `${viewTabs()} ${simpleSearch()}`,
'html-sidebar': sidebarWithPortals(),
'html-navigation-heading': 'Navigation menu',
'html-logo-attributes': `class="mw-wiki-logo" href="/wiki/Main_Page" title="Visit the main page"`
}
NAVIGATION_TEMPLATE_DATA.loggedOutWithVariants,
NAVIGATION_TEMPLATE_PARTIALS
);
export const navigationLoggedInWithMore = () => mustache.render( navTemplate,
{
'html-personalmenu': loggedInWithEcho(),
'html-navigation-left-tabs': namespaceTabs(),
'html-navigation-right-tabs': `${viewTabs()} ${more()} ${simpleSearch()}`,
'html-sidebar': sidebarWithPortals(),
'html-navigation-heading': 'Navigation menu',
'html-logo-attributes': `class="mw-wiki-logo" href="/wiki/Main_Page" title="Visit the main page"`
}
NAVIGATION_TEMPLATE_DATA.loggedInWithMoreActions,
NAVIGATION_TEMPLATE_PARTIALS
);

View File

@ -0,0 +1,32 @@
import personalMenuTemplate from '!!raw-loader!../includes/templates/PersonalMenu.mustache';
import { htmluserlangattributes } from './utils';
const loggedOut = {
'msg-label': 'Personal tools',
'html-userlangattributes': htmluserlangattributes,
'html-loggedin': '<li id="pt-anonuserpage">Not logged in</li>',
'html-personal-tools': `<li id="pt-anontalk"><a href="/wiki/Special:MyTalk" title="Discussion about edits from this IP address [⌃⌥n]" accesskey="n">Talk</a></li><li id="pt-anoncontribs"><a href="/wiki/Special:MyContributions" title="A list of edits made from this IP address [⌃⌥y]" accesskey="y">Contributions</a></li><li id="pt-createaccount"><a href="/w/index.php?title=Special:CreateAccount&amp;returnto=Main+Page" title="You are encouraged to create an account and log in; however, it is not mandatory">Create account</a></li><li id="pt-login"><a href="/w/index.php?title=Special:UserLogin&amp;returnto=Main+Page" title="You're encouraged to log in; however, it's not mandatory. [⌃⌥o]" accesskey="o">Log in</a></li>`
};
const loggedInWithEcho = {
'msg-label': 'Personal tools',
'html-userlangattributes': htmluserlangattributes,
'html-loggedin': '',
'html-personal-tools': `<li id="pt-userpage"><a href="/wiki/User:Jdlrobson" dir="auto" title="Your user page [⌃⌥.]" accesskey=".">Jdlrobson</a></li><li id="pt-notifications-alert"><a href="/wiki/Special:Notifications" class="mw-echo-notifications-badge mw-echo-notification-badge-nojs oo-ui-icon-bell mw-echo-notifications-badge-all-read" data-counter-num="0" data-counter-text="0" title="Your alerts">Alerts (0)</a></li><li id="pt-notifications-notice"><a href="/wiki/Special:Notifications" class="mw-echo-notifications-badge mw-echo-notification-badge-nojs oo-ui-icon-tray" data-counter-num="3" data-counter-text="3" title="Your notices">Notices (3)</a></li><li id="pt-mytalk"><a href="/wiki/User_talk:Jdlrobson" title="Your talk page [⌃⌥n]" accesskey="n">Talk</a></li><li id="pt-sandbox"><a href="/wiki/User:Jdlrobson/sandbox" title="Your sandbox">Sandbox</a></li><li id="pt-preferences"><a href="/wiki/Special:Preferences" title="Your preferences">Preferences</a></li><li id="pt-betafeatures"><a href="/wiki/Special:Preferences#mw-prefsection-betafeatures" title="Beta features">Beta</a></li><li id="pt-watchlist"><a href="/wiki/Special:Watchlist" title="A list of pages you are monitoring for changes [⌃⌥l]" accesskey="l">Watchlist</a></li><li id="pt-mycontris"><a href="/wiki/Special:Contributions/Jdlrobson" title="A list of your contributions [⌃⌥y]" accesskey="y">Contributions</a></li><li id="pt-logout"><a href="/w/index.php?title=Special:UserLogout&amp;returnto=Main+Page&amp;returntoquery=useskin%3Dvector" title="Log out">Log out</a></li>`
};
const loggedInWithULS = {
'msg-label': 'Personal tools',
'html-userlangattributes': htmluserlangattributes,
'html-lang-selector': '<li class="uls-trigger active"><a href="#">English</a></li>',
'html-loggedin': '',
'html-personal-tools': `<li id="pt-userpage"><a href="/wiki/User:Jdlrobson" dir="auto" title="Your user page [⌃⌥.]" accesskey=".">Jdlrobson</a></li><li id="pt-notifications-alert"><a href="/wiki/Special:Notifications" class="mw-echo-notifications-badge mw-echo-notification-badge-nojs oo-ui-icon-bell mw-echo-notifications-badge-all-read" data-counter-num="0" data-counter-text="0" title="Your alerts">Alerts (0)</a></li><li id="pt-notifications-notice"><a href="/wiki/Special:Notifications" class="mw-echo-notifications-badge mw-echo-notification-badge-nojs oo-ui-icon-tray" data-counter-num="3" data-counter-text="3" title="Your notices">Notices (3)</a></li><li id="pt-mytalk"><a href="/wiki/User_talk:Jdlrobson" title="Your talk page [⌃⌥n]" accesskey="n">Talk</a></li><li id="pt-sandbox"><a href="/wiki/User:Jdlrobson/sandbox" title="Your sandbox">Sandbox</a></li><li id="pt-preferences"><a href="/wiki/Special:Preferences" title="Your preferences">Preferences</a></li><li id="pt-betafeatures"><a href="/wiki/Special:Preferences#mw-prefsection-betafeatures" title="Beta features">Beta</a></li><li id="pt-watchlist"><a href="/wiki/Special:Watchlist" title="A list of pages you are monitoring for changes [⌃⌥l]" accesskey="l">Watchlist</a></li><li id="pt-mycontris"><a href="/wiki/Special:Contributions/Jdlrobson" title="A list of your contributions [⌃⌥y]" accesskey="y">Contributions</a></li><li id="pt-logout"><a href="/w/index.php?title=Special:UserLogout&amp;returnto=Main+Page&amp;returntoquery=useskin%3Dvector" title="Log out">Log out</a></li>`
};
const PERSONAL_MENU_TEMPLATE_DATA = {
loggedOut,
loggedInWithEcho,
loggedInWithULS
};
export { PERSONAL_MENU_TEMPLATE_DATA, personalMenuTemplate };

View File

@ -1,6 +1,5 @@
import mustache from 'mustache';
import personalMenu from '!!raw-loader!../includes/templates/PersonalMenu.mustache';
import { htmluserlangattributes } from './utils';
import { personalMenuTemplate, PERSONAL_MENU_TEMPLATE_DATA } from './personalNavigation.stories.data';
import '../resources/skins.vector.styles/personalNavigation.less';
import '../.storybook/common.less';
@ -8,24 +7,11 @@ export default {
title: 'Personal Navigation'
};
export const loggedOut = () => mustache.render( personalMenu, {
'msg-label': 'Personal tools',
'html-userlangattributes': htmluserlangattributes,
'html-loggedin': '<li id="pt-anonuserpage">Not logged in</li>',
'html-personal-tools': `<li id="pt-anontalk"><a href="/wiki/Special:MyTalk" title="Discussion about edits from this IP address [⌃⌥n]" accesskey="n">Talk</a></li><li id="pt-anoncontribs"><a href="/wiki/Special:MyContributions" title="A list of edits made from this IP address [⌃⌥y]" accesskey="y">Contributions</a></li><li id="pt-createaccount"><a href="/w/index.php?title=Special:CreateAccount&amp;returnto=Main+Page" title="You are encouraged to create an account and log in; however, it is not mandatory">Create account</a></li><li id="pt-login"><a href="/w/index.php?title=Special:UserLogin&amp;returnto=Main+Page" title="You're encouraged to log in; however, it's not mandatory. [⌃⌥o]" accesskey="o">Log in</a></li>`
} );
export const loggedOut = () => mustache.render( personalMenuTemplate,
PERSONAL_MENU_TEMPLATE_DATA.loggedOut );
export const loggedInWithEcho = () => mustache.render( personalMenu, {
'msg-label': 'Personal tools',
'html-userlangattributes': htmluserlangattributes,
'html-loggedin': '',
'html-personal-tools': `<li id="pt-userpage"><a href="/wiki/User:Jdlrobson" dir="auto" title="Your user page [⌃⌥.]" accesskey=".">Jdlrobson</a></li><li id="pt-notifications-alert"><a href="/wiki/Special:Notifications" class="mw-echo-notifications-badge mw-echo-notification-badge-nojs oo-ui-icon-bell mw-echo-notifications-badge-all-read" data-counter-num="0" data-counter-text="0" title="Your alerts">Alerts (0)</a></li><li id="pt-notifications-notice"><a href="/wiki/Special:Notifications" class="mw-echo-notifications-badge mw-echo-notification-badge-nojs oo-ui-icon-tray" data-counter-num="3" data-counter-text="3" title="Your notices">Notices (3)</a></li><li id="pt-mytalk"><a href="/wiki/User_talk:Jdlrobson" title="Your talk page [⌃⌥n]" accesskey="n">Talk</a></li><li id="pt-sandbox"><a href="/wiki/User:Jdlrobson/sandbox" title="Your sandbox">Sandbox</a></li><li id="pt-preferences"><a href="/wiki/Special:Preferences" title="Your preferences">Preferences</a></li><li id="pt-betafeatures"><a href="/wiki/Special:Preferences#mw-prefsection-betafeatures" title="Beta features">Beta</a></li><li id="pt-watchlist"><a href="/wiki/Special:Watchlist" title="A list of pages you are monitoring for changes [⌃⌥l]" accesskey="l">Watchlist</a></li><li id="pt-mycontris"><a href="/wiki/Special:Contributions/Jdlrobson" title="A list of your contributions [⌃⌥y]" accesskey="y">Contributions</a></li><li id="pt-logout"><a href="/w/index.php?title=Special:UserLogout&amp;returnto=Main+Page&amp;returntoquery=useskin%3Dvector" title="Log out">Log out</a></li>`
} );
export const loggedInWithEcho = () => mustache.render( personalMenuTemplate,
PERSONAL_MENU_TEMPLATE_DATA.loggedInWithEcho );
export const loggedInWithULS = () => mustache.render( personalMenu, {
'msg-label': 'Personal tools',
'html-userlangattributes': htmluserlangattributes,
'html-lang-selector': '<li class="uls-trigger active"><a href="#">English</a></li>',
'html-loggedin': '',
'html-personal-tools': `<li id="pt-userpage"><a href="/wiki/User:Jdlrobson" dir="auto" title="Your user page [⌃⌥.]" accesskey=".">Jdlrobson</a></li><li id="pt-notifications-alert"><a href="/wiki/Special:Notifications" class="mw-echo-notifications-badge mw-echo-notification-badge-nojs oo-ui-icon-bell mw-echo-notifications-badge-all-read" data-counter-num="0" data-counter-text="0" title="Your alerts">Alerts (0)</a></li><li id="pt-notifications-notice"><a href="/wiki/Special:Notifications" class="mw-echo-notifications-badge mw-echo-notification-badge-nojs oo-ui-icon-tray" data-counter-num="3" data-counter-text="3" title="Your notices">Notices (3)</a></li><li id="pt-mytalk"><a href="/wiki/User_talk:Jdlrobson" title="Your talk page [⌃⌥n]" accesskey="n">Talk</a></li><li id="pt-sandbox"><a href="/wiki/User:Jdlrobson/sandbox" title="Your sandbox">Sandbox</a></li><li id="pt-preferences"><a href="/wiki/Special:Preferences" title="Your preferences">Preferences</a></li><li id="pt-betafeatures"><a href="/wiki/Special:Preferences#mw-prefsection-betafeatures" title="Beta features">Beta</a></li><li id="pt-watchlist"><a href="/wiki/Special:Watchlist" title="A list of pages you are monitoring for changes [⌃⌥l]" accesskey="l">Watchlist</a></li><li id="pt-mycontris"><a href="/wiki/Special:Contributions/Jdlrobson" title="A list of your contributions [⌃⌥y]" accesskey="y">Contributions</a></li><li id="pt-logout"><a href="/w/index.php?title=Special:UserLogout&amp;returnto=Main+Page&amp;returntoquery=useskin%3Dvector" title="Log out">Log out</a></li>`
} );
export const loggedInWithULS = () => mustache.render( personalMenuTemplate,
PERSONAL_MENU_TEMPLATE_DATA.loggedInWithULS );

View File

@ -0,0 +1,15 @@
import searchBoxTemplate from '!!raw-loader!../includes/templates/SearchBox.mustache';
import { htmluserlangattributes } from './utils';
export { searchBoxTemplate };
export const searchBoxData = {
searchActionURL: '/w/index.php',
searchHeaderAttrsHTML: htmluserlangattributes,
searchInputLabel: 'Search',
searchDivID: 'simpleSearch',
searchInputHTML: '<input type="search" name="search" placeholder="Search Wikipedia" title="Search Wikipedia [⌃⌥f]" accesskey="f" id="searchInput" autocomplete="off">',
titleHTML: '<input type="hidden" value="Special:Search" name="title">',
fallbackSearchButtonHTML: '<input type="submit" name="fulltext" value="Search" title="Search pages for this text" id="mw-searchButton" class="searchButton mw-fallbackSearchButton"/>',
searchButtonHTML: '<input type="submit" name="go" value="Go" title="Go to a page with this exact name if it exists" id="searchButton" class="searchButton">'
};

View File

@ -1,19 +1,10 @@
import mustache from 'mustache';
import searchBox from '!!raw-loader!../includes/templates/SearchBox.mustache';
import '../resources/skins.vector.styles/search.less';
import '../.storybook/common.less';
import { searchBoxData, searchBoxTemplate } from './searchBox.stories.data';
export default {
title: 'Search'
};
export const simpleSearch = () => mustache.render( searchBox, {
searchActionURL: '/w/index.php',
searchHeaderAttrsHTML: 'dir="ltr" lang="en-GB"',
searchInputLabel: 'Search',
searchDivID: 'simpleSearch',
searchInputHTML: '<input type="search" name="search" placeholder="Search Wikipedia" title="Search Wikipedia [⌃⌥f]" accesskey="f" id="searchInput" autocomplete="off">',
titleHTML: '<input type="hidden" value="Special:Search" name="title">',
fallbackSearchButtonHTML: '<input type="submit" name="fulltext" value="Search" title="Search pages for this text" id="mw-searchButton" class="searchButton mw-fallbackSearchButton"/>',
searchButtonHTML: '<input type="submit" name="go" value="Go" title="Go to a page with this exact name if it exists" id="searchButton" class="searchButton">'
} );
export const simpleSearch = () => mustache.render( searchBoxTemplate, searchBoxData );

View File

@ -0,0 +1,39 @@
import sidebarTemplate from '!!raw-loader!../includes/templates/Sidebar.mustache';
import portalTemplate from '!!raw-loader!../includes/templates/Portal.mustache';
import { PORTALS } from './portal.stories.data';
const HTML_LOGO_ATTRIBUTES = `class="mw-wiki-logo" href="/wiki/Main_Page" title="Visit the main page"`;
const SIDEBAR_BEFORE_OUTPUT_HOOKINFO = `Beware: Portals can be added, removed or reordered using
SidebarBeforeOutput hook as in this example.`;
export { sidebarTemplate };
export const SIDEBAR_TEMPLATE_PARTIALS = {
Portal: portalTemplate
};
export const SIDEBAR_DATA = {
withNoPortals: {
'array-portals': [],
'html-logo-attributes': HTML_LOGO_ATTRIBUTES
},
withPortals: {
'array-portals': [
PORTALS.navigation,
PORTALS.toolbox,
PORTALS.otherProjects,
PORTALS.langlinks
],
'html-logo-attributes': HTML_LOGO_ATTRIBUTES
},
thirdParty: {
'array-portals': [
PORTALS.toolbox,
PORTALS.navigation,
{
'html-portal-content': SIDEBAR_BEFORE_OUTPUT_HOOKINFO
}
],
'html-logo-attributes': HTML_LOGO_ATTRIBUTES
}
};

View File

@ -1,51 +1,21 @@
import mustache from 'mustache';
import sidebarTemplate from '!!raw-loader!../includes/templates/Sidebar.mustache';
import portalTemplate from '!!raw-loader!../includes/templates/Portal.mustache';
import { PORTALS } from './portal.stories.data';
import '../.storybook/common.less';
import '../resources/skins.vector.styles/navigation.less';
const HTML_LOGO_ATTRIBUTES = `class="mw-wiki-logo" href="/wiki/Main_Page" title="Visit the main page"`;
const SIDEBAR_BEFORE_OUTPUT_HOOKINFO = `Beware: Portals can be added, removed or reordered using
SidebarBeforeOutput hook as in this example.`;
import { sidebarTemplate, SIDEBAR_DATA, SIDEBAR_TEMPLATE_PARTIALS } from './sidebar.stories.data';
export default {
title: 'Sidebar'
};
export const sidebarWithNoPortals = () => mustache.render( sidebarTemplate,
{
'array-portals': [],
'html-logo-attributes': HTML_LOGO_ATTRIBUTES
}
export const sidebarWithNoPortals = () => mustache.render(
sidebarTemplate, SIDEBAR_DATA.withNoPortals, SIDEBAR_TEMPLATE_PARTIALS
);
export const sidebarWithPortals = () => mustache.render( sidebarTemplate,
{
'array-portals': [
PORTALS.navigation,
PORTALS.toolbox,
PORTALS.otherProjects,
PORTALS.langlinks
],
'html-logo-attributes': HTML_LOGO_ATTRIBUTES
},
{
Portal: portalTemplate
}
export const sidebarWithPortals = () => mustache.render(
sidebarTemplate, SIDEBAR_DATA.withPortals, SIDEBAR_TEMPLATE_PARTIALS
);
export const sidebarThirdParty = () => mustache.render( sidebarTemplate,
{
'array-portals': [
PORTALS.toolbox,
PORTALS.navigation,
{
'html-portal-content': SIDEBAR_BEFORE_OUTPUT_HOOKINFO
}
],
'html-logo-attributes': HTML_LOGO_ATTRIBUTES
},
{
Portal: portalTemplate
}
export const sidebarThirdParty = () => mustache.render(
sidebarTemplate, SIDEBAR_DATA.thirdParty, SIDEBAR_TEMPLATE_PARTIALS
);

View File

@ -4,8 +4,13 @@ import skinTemplate from '!!raw-loader!../includes/templates/index.mustache';
import { placeholder } from './utils';
import '../resources/skins.vector.styles/screen.less';
import { navigationLoggedOutWithVariants, navigationLoggedInWithMore } from './navigation.stories';
import { footer } from './footer.stories';
import { NAVIGATION_TEMPLATE_DATA, navTemplate, NAVIGATION_TEMPLATE_PARTIALS } from './navigation.stories.data';
import { FOOTER_TEMPLATE_DATA, footerTemplate } from './footer.stories.data';
const TEMPLATE_PARTIALS = Object.assign( {}, NAVIGATION_TEMPLATE_PARTIALS, {
Navigation: navTemplate,
Footer: footerTemplate
} );
export default {
title: 'Skin'
@ -37,10 +42,10 @@ export const vector2019LoggedOut = () => mustache.render( skinTemplate, {
'html-userlangattributes': htmluserlangattributes,
'msg-jumptonavigation': 'Jump to navigation',
'msg-jumptosearch': 'Jump to search',
'html-navigation': navigationLoggedOutWithVariants(),
'data-navigation': NAVIGATION_TEMPLATE_DATA.loggedOutWithVariants,
// site specific
'html-footer': footer(),
'data-footer': FOOTER_TEMPLATE_DATA,
'html-sitenotice': placeholder( 'a site notice or central notice banner may go here', 70 ),
// article dependent
@ -53,7 +58,7 @@ export const vector2019LoggedOut = () => mustache.render( skinTemplate, {
'html-dataAfterContent': placeholder( 'Extensions can add here e.g. Related Articles.', 100 ),
'html-indicators': HTML_INDICATORS,
'html-subtitle': placeholder( 'Extensions can configure subtitle', 20 )
} );
}, TEMPLATE_PARTIALS );
export const vector2019LoggedIn = () => mustache.render( skinTemplate, {
'html-title': 'Vector 2019',
@ -62,14 +67,14 @@ export const vector2019LoggedIn = () => mustache.render( skinTemplate, {
'html-userlangattributes': htmluserlangattributes,
'msg-jumptonavigation': 'Jump to navigation',
'msg-jumptosearch': 'Jump to search',
'html-navigation': navigationLoggedInWithMore(),
'data-navigation': NAVIGATION_TEMPLATE_DATA.loggedInWithMoreActions,
// site specific
'html-footer': footer(),
'data-footer': FOOTER_TEMPLATE_DATA,
'html-sitenotice': placeholder( 'a site notice or central notice banner may go here', 70 ),
// article dependent
'html-bodycontent': placeholder( 'Article content goes here' ),
'html-printfooter': `Retrieved from <a dir="ltr" href="#">https://en.wikipedia.org/w/index.php?title=this&oldid=blah</a>`,
'html-catlinks': placeholder( 'Category links component from mediawiki core', 50 )
} );
}, TEMPLATE_PARTIALS );

View File

@ -0,0 +1,33 @@
import { htmluserlangattributes } from './utils';
import vectorTabsTemplate from '!!raw-loader!../includes/templates/VectorTabs.mustache';
export { vectorTabsTemplate };
export const pageActionsData = {
'tabs-id': 'p-views',
'empty-portlet': '',
'label-id': 'p-views-label',
'msg-label': 'Views',
'html-userlangattributes': htmluserlangattributes,
'html-items': `<li id="ca-view" class="collapsible selected">
<a href="/wiki/Main_Page">Read</a>
</li>
<li id="ca-viewsource" class="collapsible">
<a href="/w/index.php?title=Main_Page&amp;action=edit" title="This page is protected.
You can view its source [e]" accesskey="e">View source</a></li>
<li id="ca-history" class="collapsible">
<a href="/w/index.php?title=Main_Page&amp;action=history" title="Past revisions of this page [⌃⌥h]" accesskey="h">View history</a>
</li>
<li id="ca-unwatch" class="collapsible icon mw-watchlink"><a href="/w/index.php?title=Main_Page&amp;action=unwatch" data-mw="interface" title="Remove this page from your watchlist [⌃⌥w]" accesskey="w">Unwatch</a></li>
`
};
export const namespaceTabsData = {
'tabs-id': 'p-namespaces',
'empty-portlet': '',
'label-id': 'p-namespaces-label',
'msg-label': 'Namespaces',
'html-userlangattributes': htmluserlangattributes,
'html-items': `<li id="ca-nstab-main" class="selected"><a href="/wiki/Main_Page" title="View the content page [⌃⌥c]" accesskey="c">Main page</a></li>
<li id="ca-talk"><a href="/wiki/Talk:Main_Page" rel="discussion" title="Discussion about the content page [⌃⌥t]" accesskey="t">Talk (3)</a></li>`
};

View File

@ -1,6 +1,5 @@
import mustache from 'mustache';
import { htmluserlangattributes } from './utils';
import vectorTabsTemplate from '!!raw-loader!../includes/templates/VectorTabs.mustache';
import { namespaceTabsData, pageActionsData, vectorTabsTemplate } from './tabs.stories.data';
import '../resources/skins.vector.styles/tabs.less';
import '../.storybook/common.less';
@ -8,31 +7,6 @@ export default {
title: 'Tabs'
};
export const viewTabs = () => mustache.render( vectorTabsTemplate, {
'tabs-id': 'p-views',
'empty-portlet': '',
'label-id': 'p-views-label',
'msg-label': 'Views',
'html-userlangattributes': htmluserlangattributes,
'html-items': `<li id="ca-view" class="collapsible selected">
<a href="/wiki/Main_Page">Read</a>
</li>
<li id="ca-viewsource" class="collapsible">
<a href="/w/index.php?title=Main_Page&amp;action=edit" title="This page is protected.
You can view its source [e]" accesskey="e">View source</a></li>
<li id="ca-history" class="collapsible">
<a href="/w/index.php?title=Main_Page&amp;action=history" title="Past revisions of this page [⌃⌥h]" accesskey="h">View history</a>
</li>
<li id="ca-unwatch" class="collapsible icon mw-watchlink"><a href="/w/index.php?title=Main_Page&amp;action=unwatch" data-mw="interface" title="Remove this page from your watchlist [⌃⌥w]" accesskey="w">Unwatch</a></li>
`
} );
export const pageActionTabs = () => mustache.render( vectorTabsTemplate, pageActionsData );
export const namespaceTabs = () => mustache.render( vectorTabsTemplate, {
'tabs-id': 'p-namespaces',
'empty-portlet': '',
'label-id': 'p-namespaces-label',
'msg-label': 'Namespaces',
'html-userlangattributes': htmluserlangattributes,
'html-items': `<li id="ca-nstab-main" class="selected"><a href="/wiki/Main_Page" title="View the content page [⌃⌥c]" accesskey="c">Main page</a></li>
<li id="ca-talk"><a href="/wiki/Talk:Main_Page" rel="discussion" title="Discussion about the content page [⌃⌥t]" accesskey="t">Talk (3)</a></li>`
} );
export const namespaceTabs = () => mustache.render( vectorTabsTemplate, namespaceTabsData );