8.6.0 • Published 3 years ago

@transferwise/public-navigation v8.6.0

Weekly downloads
-
License
ISC
Repository
github
Last release
3 years ago

:sailboat: Public navigation components

npm GitHub release CircleCI npm

Public navigation components to be used on all TransferWise public pages.

See the components live here.

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

personal-nav

BusinessNavigation

business-nav

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.

  1. personal_nav: Items that are only used in PersonalNavigation
  2. business_nav: Items that are only used in BusinessNavigation
  3. button-items: Items that are used in both the navigation components
  4. footer: Items that are only used in Footer

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 for BusinessNavigation
  • personal_nav: only for PersonalNavigation
  • common: used in both BusinessNavigation/PersonalNavigation
  • footer: only for Footer

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:

  1. Add the language to the Crowdin project's "Target languages"

  2. 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 to nl).
    • 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.

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 card
  • SUPPORTED_CARD_LOCALES: Locations where users can get balances/bank details and the plastic card
  • SUPPORTED_CARD_WAITLIST_LOCALES: Locations where users can...
  • SUPPORTED_BUSINESS_LOCALES: Locations where businesses can get some (at least 1) of our business products
  • SUPPORTED_BUSINESS_BORDERLESS_LOCALES: Locations where businesses can get balances but no plastic card
  • SUPPORTED_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

  1. Run tests with npm run jest. npm test will check for package and changelog version match, ESLint and Prettier format in addition.
  2. Develop using npm start on port 9000.
  3. Bump version number in package.json according to semver and add an item that a release will be based on to CHANGELOG.md.
  4. Submit your pull request from a feature branch and get code reviewed. Docs for your branch will automatically be deployed to gh-pages.
  5. Post your pull request to #conversion-pull or #organic-growth-dev
  6. If the pull request is approved and the CircleCI build passes, you will be able to squash and merge.
  7. 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.

8.6.0

3 years ago

8.5.0

3 years ago

8.4.1

3 years ago

8.4.0

3 years ago

8.4.3

3 years ago

8.4.2

3 years ago

8.3.0

3 years ago

8.2.0

3 years ago

8.1.6

3 years ago

8.1.5

3 years ago

8.1.4

3 years ago

8.1.3

3 years ago

8.1.2

3 years ago

8.1.1

3 years ago

7.0.7

3 years ago

8.1.0

3 years ago

8.0.0

3 years ago

8.0.0-beta.3

3 years ago

7.0.6

3 years ago

8.0.0-beta.2

3 years ago

8.0.0-beta.1

3 years ago

7.0.5

3 years ago

7.0.4

3 years ago

7.0.3

4 years ago

7.0.2

4 years ago

7.0.1

4 years ago

6.2.1

4 years ago

6.2.0

4 years ago

6.1.0

4 years ago

6.0.0-beta.1

4 years ago

6.0.0

4 years ago

5.0.1

4 years ago

5.0.0

4 years ago

4.1.1

4 years ago

4.1.0

4 years ago

4.0.2-beta.2

4 years ago

4.0.2

4 years ago

4.0.1

4 years ago

4.0.0

4 years ago

3.14.1

4 years ago

3.14.0

4 years ago

3.13.2

4 years ago

3.13.1

4 years ago

3.13.0

4 years ago

3.12.7

4 years ago

3.12.6

4 years ago

3.12.5

4 years ago

3.12.4

4 years ago

3.12.3

4 years ago

3.12.2

4 years ago

3.12.1

4 years ago

3.12.0

4 years ago

3.11.2

4 years ago

3.11.1

4 years ago

3.11.0

4 years ago

3.10.1

4 years ago

3.10.0

4 years ago

3.9.0

4 years ago

3.8.0

4 years ago

3.7.11

4 years ago

3.7.10

4 years ago

3.7.9

4 years ago

3.7.8

4 years ago

3.7.7

4 years ago

3.7.6

4 years ago

3.7.5

4 years ago

3.7.4

4 years ago

3.7.3

4 years ago

3.7.2

4 years ago

3.7.1

4 years ago

3.7.0

4 years ago

3.6.0

4 years ago

3.5.2

4 years ago

3.5.1

4 years ago

3.5.0

4 years ago

3.4.4

5 years ago

3.4.3

5 years ago

3.4.2

5 years ago

3.4.1

5 years ago

3.4.0

5 years ago

3.3.10

5 years ago

3.3.9

5 years ago

3.3.8

5 years ago

3.3.7

5 years ago

3.3.6

5 years ago

3.3.4

5 years ago

3.3.3

5 years ago

3.3.2

5 years ago

3.3.1

5 years ago

3.3.0

5 years ago

3.2.4

5 years ago

3.2.3

5 years ago

3.2.2

5 years ago

3.2.1

5 years ago

3.2.0

5 years ago

3.1.10

5 years ago

3.1.9

5 years ago

3.1.8

5 years ago

3.1.7

5 years ago

3.1.6

5 years ago

3.1.5

5 years ago

3.1.4

5 years ago

3.1.3

5 years ago

3.1.2

5 years ago

3.1.1

5 years ago

3.1.0

5 years ago

3.0.0

5 years ago

2.9.0

5 years ago

2.8.0

5 years ago

2.7.1

5 years ago

2.7.0

5 years ago

2.6.0

5 years ago

2.5.2

5 years ago

2.5.1

5 years ago

2.5.0

5 years ago

2.4.1

5 years ago

2.4.0

5 years ago

2.3.0

6 years ago

2.2.0

6 years ago

2.1.0

6 years ago

2.0.0

6 years ago

1.1.4

6 years ago

1.1.3

6 years ago

1.1.2

6 years ago

1.1.1

6 years ago

1.1.0

6 years ago

1.0.1

6 years ago

1.0.0

6 years ago

0.11.8

6 years ago

0.11.7

6 years ago

0.11.6

6 years ago

0.11.5

6 years ago

0.11.4

6 years ago

0.11.3

6 years ago

0.11.2

6 years ago

0.11.1

6 years ago

0.11.0

6 years ago

0.10.0

6 years ago

0.9.6

6 years ago

0.9.5

6 years ago

0.9.4

6 years ago

0.9.3

6 years ago

0.9.2

6 years ago

0.9.1

6 years ago

0.9.0

6 years ago

0.8.1

6 years ago

0.8.0

6 years ago

0.7.1

6 years ago

0.7.0

6 years ago

0.6.2

6 years ago

0.6.1

6 years ago

0.6.0

6 years ago

0.5.1

6 years ago

0.5.0

6 years ago

0.4.0

6 years ago

0.3.3

6 years ago

0.3.2

6 years ago

0.3.1

6 years ago

0.3.0

6 years ago

0.2.2

6 years ago

0.2.1

6 years ago

0.2.0

6 years ago

0.1.0

6 years ago