Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Allow usage outside of Next.js (e.g. Jest and Storybook) #76

Merged
merged 2 commits into from
Jan 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/example-advanced/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ require('eslint-config-molindo/setupPlugins');

module.exports = {
extends: ['molindo/typescript', 'molindo/react', 'plugin:@next/next/recommended'],
env: {
node: true
},
rules: {
'react/react-in-jsx-scope': 'off',
'jsx-a11y/anchor-is-valid': 'off',
Expand Down
5 changes: 5 additions & 0 deletions packages/example-advanced/config/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Used by Jest

module.exports = {
presets: ['next/babel']
};
7 changes: 7 additions & 0 deletions packages/example-advanced/config/jest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"testEnvironment": "jsdom",
"rootDir": "../",
"transform": {
"\\.tsx$": ["babel-jest", { "configFile": "./config/babel.config.js" }]
}
}
6 changes: 4 additions & 2 deletions packages/example-advanced/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"scripts": {
"dev": "next dev",
"lint": "eslint src && tsc",
"test": "echo 'No tests yet'",
"test": "jest --config config/jest.json",
"build": "next build",
"start": "next start"
},
Expand All @@ -18,9 +18,11 @@
"react-dom": "^17.0.0"
},
"devDependencies": {
"@testing-library/react": "12.1.2",
"@types/lodash": "4.14.176",
"eslint": "7.4.0",
"eslint-config-molindo": "5.0.1",
"eslint-config-next": "^12.0.0"
"eslint-config-next": "^12.0.0",
"jest": "27.4.5"
}
}
30 changes: 30 additions & 0 deletions packages/example-advanced/src/components/Navigation.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// @ts-ignore

import {render} from '@testing-library/react';
import {pick} from 'lodash';
import {NextIntlProvider} from 'next-intl';
import messages from '../../messages/en.json';
import Navigation from './Navigation';

// If the tested component uses features from Next.js, you have to mock them.
// Note that next-intl only has an optional dependency on Next.js.
jest.mock('next/router', () => ({
useRouter() {
return {
route: '/',
locale: 'en',
locales: ['en', 'de']
};
}
}));

it('renders', () => {
render(
<NextIntlProvider
locale="en"
messages={pick(messages, Navigation.messages)}
>
<Navigation />
</NextIntlProvider>
);
});
2 changes: 1 addition & 1 deletion packages/example-advanced/src/components/Navigation.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {useTranslations} from 'next-intl';
import {useRouter} from 'next/dist/client/router';
import Link from 'next/link';
import {useRouter} from 'next/router';

export default function Navigation() {
const t = useTranslations('Navigation');
Expand Down
1 change: 1 addition & 0 deletions packages/example-advanced/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
},
"include": [
"next-env.d.ts",
"**/*.js",
"**/*.ts",
"**/*.tsx"
],
Expand Down
9 changes: 7 additions & 2 deletions packages/next-intl/src/NextIntlProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ type Props = Omit<ComponentProps<typeof IntlProvider>, 'locale'> & {
};

export default function NextIntlProvider({locale, ...rest}: Props) {
const nextLocale = useRouter().locale;
if (!locale && nextLocale) locale = nextLocale;
// The router can be undefined if used in a context outside
// of Next.js (e.g. unit tests, Storybook, ...)
const nextLocale = useRouter()?.locale;

if (!locale && nextLocale) {
locale = nextLocale;
}

if (!locale) {
throw new Error(
Expand Down
6 changes: 6 additions & 0 deletions packages/website/pages/docs/faq.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,9 @@ Yes, see [`use-intl`](https://github.com/amannn/next-intl/tree/main/packages/use
This library is only concerned with formatting dates. A great library to parse and manipulate dates is [date-fns](https://date-fns.org/).

Note that parsing dates with `new Date(dateString)` and `Date.parse(dateString)` is discouraged due to browser differences and inconsistencies (see [the MDN docs on the `Date` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date)).

## Can next-intl be used with testing libraries like Jest and Storybook?

Yes. These environments pose the challenge that components render in isolation and therefore relevant providers might be missing. To fix this, you should wrap the component with `NextIntlProvider`.

Please see the [Jest example](https://github.com/amannn/next-intl/blob/main/packages/example-advanced/src/components/Navigation.spec.tsx) to explore a working setup. For Storybook, you have to [add a global decorator](https://storybook.js.org/docs/react/writing-stories/decorators#global-decorators) that configures the provider.
Loading