From ab76eedb5b589e80dd5c0dd062d06f5195b45662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Thu, 15 Jul 2021 15:33:34 +0200 Subject: [PATCH 1/4] typings --- .gitignore | 1 + package.json | 7 +++- src/index.js | 106 ++++++++++++++++++++++++++++++++++++++------------ tsconfig.json | 21 ++++++++++ 4 files changed, 109 insertions(+), 26 deletions(-) create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index e6617ff..06c52bd 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ coverage node_modules package-lock.json +/types diff --git a/package.json b/package.json index 46bbaf7..b0fff83 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@babel/preset-react": "^7.8.3", "@babel/register": "^7.8.6", "@rollup/plugin-babel": "^5.0.3", + "@types/react": "^17.0.14", "@u-wave/translate": "^1.1.0", "babel-plugin-istanbul": "^6.0.0", "cross-env": "^7.0.2", @@ -30,7 +31,8 @@ "react": "^17.0.0", "react-dom": "^17.0.0", "react-test-renderer": "^17.0.0", - "rollup": "^2.0.6" + "rollup": "^2.0.6", + "typescript": "^4.3.5" }, "homepage": "https://github.com/u-wave/react-translate#readme", "keywords": [ @@ -42,6 +44,7 @@ "license": "MIT", "main": "dist/react-translate.js", "module": "dist/react-translate.es.js", + "typings": "types/index.d.ts", "peerDependencies": { "react": "^17.0.0" }, @@ -50,7 +53,7 @@ "url": "git+https://github.com/u-wave/react-translate.git" }, "scripts": { - "prepare": "rollup -c", + "prepare": "tsc && rollup -c", "lint": "eslint --cache .", "test": "npm run lint && npm run tests-only", "tests-only": "nyc mocha --require @babel/register", diff --git a/src/index.js b/src/index.js index c16f588..beaa1ba 100644 --- a/src/index.js +++ b/src/index.js @@ -1,13 +1,33 @@ import React from 'react'; import PropTypes from 'prop-types'; -const TranslateContext = React.createContext(); -const { Provider, Consumer } = TranslateContext; +/** @typedef {{ + * t: (key: string, data: object) => string, + * parts: (key: string, data: object) => any[], + * }} Translator */ +/** @type {React.Context} */ +// @ts-ignore TS2322: the assigned type is narrower than the identifier's so this is fine +const TranslateContext = React.createContext(undefined); + +/** + * Make a translator instance available to the context. Children of this `TranslateProvider` element + * can access the translator instance using `useTranslate()` or `Interpolate` as listed below. + * + * ```js + * const translator = new Translator(...); + * + * + * + * + * ``` + * + * @param {{ translator: Translator, children: JSX.Element }} props + */ export const TranslateProvider = ({ translator, children }) => ( - + {children} - + ); /* istanbul ignore next */ if (process.env.NODE_ENV !== 'production') { @@ -20,30 +40,68 @@ if (process.env.NODE_ENV !== 'production') { }; } -export const translate = () => (Component) => (props) => ( - - {(translator) => ( +/** + * Get the `@u-wave/translate` instance from the context. Destructuring the `t` function is the + * recommended way to use this instance. This can be used in place of the `translate()` HOC in + * function components to avoid introducing additional nesting and PropTypes requirements. + * + * @returns {Translator} + */ +export function useTranslator() { + const context = React.useContext(TranslateContext); + if (context === undefined) { + throw new Error('useTranslator() can only be used within a TranslateContext'); + } + return context; +} + +/** + * @template {object} TProps + * @returns {(Component: React.ComponentType) => + * React.ComponentType} + */ +export function translate() { + return (Component) => (props) => { + const { t } = useTranslator(); + + return ( - )} - -); + ); + }; +} + +/** + * Translate the key given in the `i18nKey` prop. The other props are used as the interpolation + * data. Unlike `useTranslate()`, this component can interpolate other React elements: + * + * ```js + * {name} + * )} + * /> + * ``` + * + * Here, the `name` prop is a React element, and it will be rendered correctly. + * + * @param {{ [key: string]: unknown, i18nKey: string }} props + */ +export function Interpolate({ i18nKey, ...props }) { + const { parts } = useTranslator(); + + return ( + // Manually use createElement so we're not passing an array as children to React. + // Passing the array would require us to add keys to each interpolated element + // but we know that the shape will stay the same so it's safe to spread it and act + // as if they were all written as separate children by the user. + React.createElement(React.Fragment, {}, ...parts(i18nKey, props)) + ); +} -export const useTranslator = () => React.useContext(TranslateContext); - -export const Interpolate = (props) => ( - - {(translator) => ( - // Manually use createElement so we're not passing an array as children to React. - // Passing the array would require us to add keys to each interpolated element - // but we know that the shape will stay the same so it's safe to spread it and act - // as if they were all written as separate children by the user. - React.createElement(React.Fragment, {}, ...translator.parts(props.i18nKey, props)) - )} - -); /* istanbul ignore next */ if (process.env.NODE_ENV !== 'production') { Interpolate.propTypes = { diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2e006d7 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "jsx": "preserve", + "esModuleInterop": true, + "strict": false, + "strictBindCallApply": true, + "strictNullChecks": true, + "noImplicitThis": true, + "allowJs": true, + "checkJs": true, + "outDir": "types", + "declaration": true, + "emitDeclarationOnly": true + }, + "include": [ + "src/**/*.js" + ], + "exclude": [ + "node_modules" + ] +} From 872b12b8152ab59cfb0050ee9562af3431be7df1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Thu, 15 Jul 2021 15:40:40 +0200 Subject: [PATCH 2/4] fix `this` --- src/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index beaa1ba..0128d2d 100644 --- a/src/index.js +++ b/src/index.js @@ -91,14 +91,14 @@ export function translate() { * @param {{ [key: string]: unknown, i18nKey: string }} props */ export function Interpolate({ i18nKey, ...props }) { - const { parts } = useTranslator(); + const translator = useTranslator(); return ( // Manually use createElement so we're not passing an array as children to React. // Passing the array would require us to add keys to each interpolated element // but we know that the shape will stay the same so it's safe to spread it and act // as if they were all written as separate children by the user. - React.createElement(React.Fragment, {}, ...parts(i18nKey, props)) + React.createElement(React.Fragment, {}, ...translator.parts(i18nKey, props)) ); } From 2d45af13f16440f3990f5f1de891397a6baa141b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Mon, 22 Nov 2021 14:39:13 +0100 Subject: [PATCH 3/4] tweaks --- src/index.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/index.js b/src/index.js index 0128d2d..fed3e41 100644 --- a/src/index.js +++ b/src/index.js @@ -22,13 +22,15 @@ const TranslateContext = React.createContext(undefined); * * ``` * - * @param {{ translator: Translator, children: JSX.Element }} props + * @param {{ translator: Translator, children: React.ReactNode }} props */ -export const TranslateProvider = ({ translator, children }) => ( - - {children} - -); +export function TranslateProvider({ translator, children }) { + return ( + + {children} + + ); +} /* istanbul ignore next */ if (process.env.NODE_ENV !== 'production') { TranslateProvider.propTypes = { @@ -56,6 +58,11 @@ export function useTranslator() { } /** + * Get the translate function from the context. This is a higher-order component, only intended + * for use in class components. If you can, use `useTranslator()` instead. + * + * The translate function is passed in as the `t` prop. + * * @template {object} TProps * @returns {(Component: React.ComponentType) => * React.ComponentType} From 452b57451e39ac7595aac21a14859b6f84310d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Sun, 24 Apr 2022 15:16:44 +0200 Subject: [PATCH 4/4] force npm i --- .github/workflows/ci.yml | 4 ++-- .github/workflows/deploy.yml | 2 +- example/package.json | 6 ++---- package.json | 7 +------ 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e40d7a..6c44486 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: with: node-version: lts/* - name: Install dependencies - run: npm install --legacy-peer-deps + run: npm install --force - name: Check code style run: npm run lint test: @@ -32,7 +32,7 @@ jobs: with: node-version: ${{matrix.node-version}} - name: Install dependencies - run: npm install --legacy-peer-deps + run: npm install --force - name: Install React ${{matrix.react-version}} if: matrix.react-version != '18.x' run: | diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b53054f..56772ea 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -14,7 +14,7 @@ jobs: with: node-version: lts/* - name: Install - run: npm install --legacy-peer-deps + run: npm install --force - name: Run tests run: npm test - name: Build example diff --git a/example/package.json b/example/package.json index f867ff4..d30159e 100644 --- a/example/package.json +++ b/example/package.json @@ -1,6 +1,6 @@ { "private": true, - "name": "react-translate-example", + "name": "@u-wave/react-translate-example", "description": "React-Translate example.", "version": "0.0.0-example", "scripts": { @@ -12,10 +12,8 @@ "@u-wave/react-translate": "file:..", "@u-wave/translate": "^1.0.0", "ecstatic": "^4.0.0", + "esbuild": "^0.14.29", "react": "^18.0.0", "react-dom": "^18.0.0" - }, - "devDependencies": { - "esbuild": "^0.14.29" } } diff --git a/package.json b/package.json index dd83a44..9d359c1 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@rollup/plugin-babel": "^5.0.3", "@types/react": "^17.0.14", "@u-wave/translate": "^1.1.0", + "@u-wave/react-translate-example": "file:example", "babel-plugin-istanbul": "^6.0.0", "cross-env": "^7.0.2", "eslint": "^8.3.0", @@ -59,11 +60,5 @@ "tests-only": "nyc mocha --require @babel/register", "example": "npm install && cd example && npm install" }, - "workspaces": { - "packages": [ - ".", - "./example" - ] - }, "sideEffects": false }