Skip to content

Commit

Permalink
fix(react): process falsy values with JSX semantics (#1906)
Browse files Browse the repository at this point in the history
  • Loading branch information
thekip committed Apr 5, 2024
1 parent 4c494ad commit 6bb8983
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 12 deletions.
54 changes: 54 additions & 0 deletions packages/react/src/Trans.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,38 @@ describe("Trans component", () => {
})
})

it("should follow jsx semantics regarding booleans", () => {
expect(
html(
<Trans
id="unknown"
message={"foo <0>{0}</0> bar"}
values={{
0: false && "lol",
}}
components={{
0: <span />,
}}
/>
)
).toEqual("foo <span></span> bar")

expect(
html(
<Trans
id="unknown"
message={"foo <0>{0}</0> bar"}
values={{
0: "lol",
}}
components={{
0: <span />,
}}
/>
)
).toEqual("foo <span>lol</span> bar")
})

it("should render default string", () => {
expect(text(<Trans id="unknown" />)).toEqual("unknown")

Expand Down Expand Up @@ -271,6 +303,28 @@ describe("Trans component", () => {
expect(translation).toEqual("1,00 €")
})

it("should render plural", () => {
const render = (count: number) =>
html(
<Trans
id={"tYX0sm"}
message={
"{count, plural, =0 {Zero items} one {# item} other {# <0>A lot of them</0>}}"
}
values={{
count,
}}
components={{
0: <a href="/more" />,
}}
/>
)

expect(render(0)).toEqual("Zero items")
expect(render(1)).toEqual("1 item")
expect(render(2)).toEqual(`2 <a href="/more">A lot of them</a>`)
})

describe("rendering", () => {
it("should render a text node with no wrapper element", () => {
const txt = html(<Trans id="Some text" />)
Expand Down
27 changes: 15 additions & 12 deletions packages/react/src/TransNoContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,28 +58,31 @@ export function TransNoContext(

if (values) {
/*
Related discussion: https://github.com/lingui/js-lingui/issues/183
Replace values placeholders with <INDEX /> and add values to `components`.
This makes them processed as JSX children and follow JSX semantics.
Related discussion: https://github.com/lingui/js-lingui/issues/1904
Values *might* contain React elements with static content.
They're replaced with <INDEX /> placeholders and added to `components`.
Another use-case is when React components directly passed as values:
Example:
Translation: Hello {name}
Translation: 'Hello {name}'
Values: { name: <strong>Jane</strong> }
It'll become "Hello <0 />" with components=[<strong>Jane</strong>]
*/
Related discussion: https://github.com/lingui/js-lingui/issues/183
*/
Object.keys(values).forEach((key) => {
const value = values[key]
const valueIsReactEl =
React.isValidElement(value) ||
(Array.isArray(value) && value.every(React.isValidElement))
if (!valueIsReactEl) return

const index = Object.keys(components).length

components[index] = value
// simple scalars should be processed as values to be able to apply formatting
if (typeof values[key] === "string" || typeof values[key] === "number") {
return
}

// react components, arrays, falsy values, all should be processed as JSX children
components[index] = <>{values[key]}</>
values[key] = `<${index}/>`
})
}
Expand Down

0 comments on commit 6bb8983

Please sign in to comment.