Skip to content

Commit

Permalink
feat: Pass props to I18nProvider.defaultComponent in Trans.render sty…
Browse files Browse the repository at this point in the history
…le (#1242)


Co-authored-by: Ilya Doroshin <[email protected]>
  • Loading branch information
ilyadoroshin and Ilya Doroshin authored Jun 22, 2022
1 parent a443fca commit fe4cac4
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 9 deletions.
26 changes: 26 additions & 0 deletions docs/ref/react.rst
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,32 @@ I18nProvider
components. :ref:`Rendering of translations <rendering-translations>` is explained
at the beginning of this document.

.. code-block:: jsx
import React from 'react';
import { I18nProvider } from '@lingui/react';
import { i18n } from '@lingui/core';
import { messages as messagesEn } from './locales/en/messages.js';
i18n.load({
en: messagesEn,
});
i18n.activate('en');
const DefaultI18n = ({ isTranslated, children }) => (
<span style={{ color: isTranslated ? undefined : 'red' }}>
{children}
</span>
)
const App = () => {
return (
<I18nProvider i18n={i18n} defaultComponent={DefaultI18n}>
// rest of the app
</I18nProvider>
);
}
``forceRenderOnLocaleChange`` is true by default and it ensures that:

- Children of ``I18nProvider`` aren't rendered before locales are loaded.
Expand Down
5 changes: 3 additions & 2 deletions packages/react/src/I18nProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React, { FunctionComponent, ComponentType, ReactNode } from "react"
import React, { ComponentType, FunctionComponent } from "react"
import { I18n } from "@lingui/core"
import { TransRenderProps } from "./Trans"

export type I18nContext = {
i18n: I18n
defaultComponent?: ComponentType<{ children?: ReactNode }>
defaultComponent?: ComponentType<TransRenderProps>
}

export type withI18nProps = {
Expand Down
51 changes: 51 additions & 0 deletions packages/react/src/Trans.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ describe("Trans component", function () {
id: "ID",
message: "Default",
translation: "Translation",
isTranslated: true,
})
})

Expand Down Expand Up @@ -241,4 +242,54 @@ describe("Trans component", function () {
expect(element).toEqual(`<div>value</div>`)
})
})

describe("I18nProvider defaulComponent accepts render-like props", function () {
const DefaultComponent: React.FunctionComponent = (props: {
children?: React.ReactNode
id: string
translation: string
message: string
isTranslated: boolean
}) => (
<>
<div data-testid="children">{props.children}</div>
{props.id && <div data-testid="id">{props.id}</div>}
{props.message && <div data-testid="message">{props.message}</div>}
{props.translation && (
<div data-testid="translation">{props.translation}</div>
)}

<div data-testid="is-translated">{String(props.isTranslated)}</div>
</>
)

it("should render defaultComponent with Trans props", function () {
const markup = render(
<I18nProvider i18n={i18n} defaultComponent={DefaultComponent}>
<Trans id="ID" message="Some message" />
</I18nProvider>
)

expect(markup.queryByTestId("id").innerHTML).toEqual("ID")
expect(markup.queryByTestId("message").innerHTML).toEqual("Some message")
expect(markup.queryByTestId("translation").innerHTML).toEqual(
"Translation"
)
expect(markup.queryByTestId("is-translated").innerHTML).toEqual("true")
})

it("should pass isTranslated: false if no translation", function () {
const markup = render(
<I18nProvider i18n={i18n} defaultComponent={DefaultComponent}>
<Trans id="NO_ID" message="Some message" />
</I18nProvider>
)

expect(markup.queryByTestId("id").innerHTML).toEqual("NO_ID")
expect(markup.queryByTestId("translation").innerHTML).toEqual(
"Some message"
)
expect(markup.queryByTestId("is-translated").innerHTML).toEqual("false")
})
})
})
24 changes: 17 additions & 7 deletions packages/react/src/Trans.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export type TransRenderProps = {
translation?: React.ReactNode
children?: React.ReactNode
message?: string | null
isTranslated?: boolean
}

export type TransProps = {
Expand Down Expand Up @@ -72,6 +73,13 @@ export function Trans(props: TransProps): React.ReactElement<any, any> | null {
const FallbackComponent = (defaultComponent ||
React.Fragment) as React.ComponentType<any>

const i18nProps = {
id,
message,
translation,
isTranslated: id !== translation && message !== translation,
}

// Validation of `render` and `component` props
if (render && component) {
console.error(
Expand All @@ -87,22 +95,24 @@ export function Trans(props: TransProps): React.ReactElement<any, any> | null {
console.error(
`Invalid value supplied to prop \`component\`. It must be a React component, provided ${component}`
)
return <FallbackComponent>{translation}</FallbackComponent>
return <FallbackComponent {...i18nProps}>{translation}</FallbackComponent>
}

// Rendering using a render prop
if (typeof render === "function") {
// Component: render={(props) => <a title={props.translation}>x</a>}
return render({
id,
translation,
message,
})
return render(i18nProps)
}

// `component` prop has a higher precedence over `defaultComponent`
const Component = (component || FallbackComponent) as React.ComponentType<any>
return <Component>{translation}</Component>
const DefaultComponent = defaultComponent

return DefaultComponent && !component ? (
<DefaultComponent {...i18nProps}>{translation}</DefaultComponent>
) : (
<Component>{translation}</Component>
)
}

Trans.defaultProps = {
Expand Down

1 comment on commit fe4cac4

@vercel
Copy link

@vercel vercel bot commented on fe4cac4 Jun 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.