@transferwise/public-navigation v8.6.0
:sailboat: Public navigation components
Public navigation components to be used on all TransferWise public pages.
Installation
npm install @transferwise/public-navigation
Usage
import { PersonalNavigation, BusinessNavigation, Footer } from '@transferwise/public-navigation';
This library exports two navigation components that you can use depending on the page you're consuming it on. If it is a business page, then you use the BusinessNavigation
export, if not, you use the PersonalNavigation
export.
PersonalNavigation
BusinessNavigation
To get active state for nav links, pass the activePath
prop to PersonalNavigation
/BusinessNavigation
so it knows which link to highlight. activePath
should be a string which matches (===
) an href
inside the nav. For localised links (e.g. /:locale/borderless
), you should include the locale inside the passed prop (so for the de
locale you'd pass activePath="/de/borderless"
).
To get push animation behavior for mobile menu, the consuming application needs to be inside a single container with the class navbar-push-container
. If not, the mobile menu will slide on top of the content, rather than push. Push is the desired behavior and should be used when possible to get consistent(ly pretty) behavior.
<PersonalNavigation skipToContentHref="#main" />
<div className="navbar-push-container">
<main id="#main">
<Page />
<Content />
</main>
<Footer />
</div>
Tests that include public-navigation
When testing components that display PersonalNavigation
or BusinessNavigation
, include the following mock for window.matchMedia()
in jest.config.js
:
Object.defineProperty(window, "matchMedia", {
writable: true,
value: jest.fn().mockImplementation((query) => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
Otherwise the render will fail with TypeError: Cannot read property 'removeListener' of null
. jsdom
mocks most features of window
, but matchMedia
is not one of them.
Adding/changing items
The components are configured as JSON files in the items
directory based on the usecase.
personal_nav
: Items that are only used inPersonalNavigation
business_nav
: Items that are only used inBusinessNavigation
button-items
: Items that are used in both the navigation componentsfooter
: Items that are only used inFooter
See information about submenus below:
Adding hidden items
It's possible to add menu items that are hidden by default. This can be useful if you want to release a new item under feature lock.
To do that, add a new entry, with an id
property and a boolean hidden
property:
{
"id": "something-cool",
"hidden": true,
"translationKey": "public-navigation.money-transfer.something-cool",
"link": "/:locale/about/link"
}
When the navigation is rendered something-cool
won't show up unless you pass the id
property to the component like this:
const revealItems = ['something-cool'];
<PersonalNavigation revealHiddenItemsList={revealItems} />;
Adding new translation strings
Translation strings live in different folder based on the use case:
business_nav
: only forBusinessNavigation
personal_nav
: only forPersonalNavigation
common
: used in bothBusinessNavigation
/PersonalNavigation
footer
: only forFooter
This is important because if you use webpack for bundling this library with your app, then only the translations required for the component you use would be added to your final bundle.
If you are using UMD bundles directly, or via dynamic imports, then tree shaking will not apply.
Check this for more info on bundling
Customizing register button link
By default, the register button in the nav redirects to /register
. You can use the registerLink
prop with the PersonalNavigation
/BusinessNavigation
component to change the default link.
i18n Internationalisation
public-navigation.js
contains all available languages.
If you want to minimize your bundle size, there's a public-navigation.${language}.js
for every language as well.
F.e. public-navigation.es.js
will only support language="es"
as a prop.
Available languages are also exposed as a named export LANGUAGES
to simplify the component's usage, as follows:
import { PersonalNavigation, LANGUAGES } from '@transferwise/public-navigation';
const language = LANGUAGES[0]; // 'es' in case of public-navigation.es.js
const FooComponent = () => (
<div>
<PersonalNavigation language={language} skipToContentHref="#main" />
</div>
);
Add support for a new language/locale/market
To make any of the components exported by the library available in a new language, follow these steps:
Add the language to the Crowdin project's "Target languages"
Add a new locale mapping in
./crowdin.yml
, e.g."nl": "nl"
or"zh-HK": "zh_HK"
- Note: This is required even if crowdin already simplifies the locale (
nl-NL
tonl
). - If this is not done, the javascript bundle will use the full locale string, as crowdin will download the messages file with the raw locale (
messages.nl-NL.json
). The goal here is to, when possible, have a two-letter code per language. A bit more information about supported languages and language codes is available on Confluence.
- Note: This is required even if crowdin already simplifies the locale (
l10n Localisation
PersonalNavigation
/BusinessNavigation
accepts a locale
prop as a string. This ensures that users only see links to products which are available in their location.
Add support for a new locale
Go to ./src/common/l10n/Locale.js
and add the new locale option to the list
Change which menu items are displayed for a locale
To update the rules for a locale, look in ./src/PublicNavigation/items/l10n/rules.js
. Each of the following is a list of locales in which the noted items will be shown. For example (at time of writing), people in Canada can get a Borderless account but not a card.
SUPPORTED_BORDERLESS_NO_CARD_LOCALES
: Locations where users can get balances/bank details but no plastic cardSUPPORTED_CARD_LOCALES
: Locations where users can get balances/bank details and the plastic cardSUPPORTED_CARD_WAITLIST_LOCALES
: Locations where users can...SUPPORTED_BUSINESS_LOCALES
: Locations where businesses can get some (at least 1) of our business productsSUPPORTED_BUSINESS_BORDERLESS_LOCALES
: Locations where businesses can get balances but no plastic cardSUPPORTED_BUSINESS_CARD_LOCALES
: Locations where businesses can get balances and cards
Fallback to gb
locale
Provide a list of locales in which content exists, using availableLocales
. If locale is not available for the item, the :locale
key will be replaced with gb
.
"items": [{
"id": "something-cool",
"availableLocales": ["us", "fr"],
"translationKey": "public-navigation.money-transfer.something-cool",
"link": "/:locale/about/link"
}]
If availableLocales
field is not provided, the item is considered to be available in all locales
Tracking
The tracking triggers are defined through the [data-tracking-id="public-navigation"]
attribute
in Google Tag Manager
(all the relevant triggers and tags contain Public navigation
in their name).
Language selector
To use language selector you have to pass language
, availableLanguages
and onLanguageChange
to the PersonalNavigation
/BusinessNavigation
component.
...
import { PersonalNavigation } from '@transferwise/public-navigation';
<PersonalNavigation
language="en"
availableLanguages={[ {value: 'en', label: 'English (UK)'}, {value: 'de', label: 'Deutsch'} ]}
onLanguageChange={newLanguage => { updateLanguageHere(newLanguage) }}
{...otherProps}
/>
...
If 1 or less available languages are passed to the navigation component or there is no onLanguageChange
passed then language selector is hidden.
Submenu
To add a second-level navigation, the component accepts a submenuItems
prop. While the items
in the main nav (and their translations) are included in the component, submenuItems
are provided by the consuming application and must be translated already.
import { PersonalNavigation } from '@transferwise/public-navigation';
const myAppsSubmenu = [
{
link: '/iban',
translatedText: 'IBAN',
isTitle: true,
},
{
link: '/iban/checker',
translatedText: 'Check an IBAN',
}
]
<PersonalNavigation
language="en"
submenuItems={myAppsSubmenu}
{...otherProps}
/>
For a more complex example with a dropdown, see ./docs/submenu-items
.
Skip to main content link
PersonalNavigation
/BusinessNavigation
requires a skipToContentHref
property and will throw an Error without one. The navigation includes an accessible link for screenreaders and keyboard users to jump to the main content of the current page. Without this link, users would have to tab
through every link in the nav on every page load. Learn more from (WebAIM on skip links).
For accessibility best practice, set an HTML ID on the main content area of the page and pass its identifier to PersonalNavigation
/BusinessNavigation
.
// Creates a link to the specified target <a href="#main">Skip to main content</a>
<PersonalNavigation skipLinkHref="#main" />
// Note: NOT id="#main". A common mistake.
<main id="main">
Page content
</main>
<Footer />
The link is only visible to sighted users on keyboard :focus
. To test this in your app, refresh the page and press tab
on the keyboard. The link should appear as a focused button below the navigation bar. Press enter
to jump to the content container you specified, or tab
again to continue tabbing through the navigation.
Contributing
- Run tests with
npm run jest
.npm test
will check for package and changelog version match, ESLint and Prettier format in addition. - Develop using
npm start
on port 9000. - Bump version number in
package.json
according to semver and add an item that a release will be based on toCHANGELOG.md
. - Submit your pull request from a feature branch and get code reviewed. Docs for your branch will automatically be deployed to gh-pages.
- Post your pull request to #conversion-pull or #organic-growth-dev
- If the pull request is approved and the CircleCI build passes, you will be able to squash and merge.
- Code will automatically be released to GitHub and published to npm according to the version specified in the changelog and
package.json
.
Other
For features and bugs, feel free to add issues or contribute.
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago