Skip to content

Commit e3ac705

Browse files
committed
feat(render): Add useRenderingOptions
1 parent cfd7e1c commit e3ac705

14 files changed

+185
-10
lines changed

packages/render/src/browser/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ export * from './render-async';
33

44
export * from '../shared/options';
55
export * from '../shared/plain-text-selectors';
6+
7+
export { useRenderingOptions } from '../shared/useRenderingOptions'

packages/render/src/browser/render-async-web.spec.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*/
44

55
import { Preview } from '../shared/utils/preview';
6-
import { Template } from '../shared/utils/template';
6+
import { Template, TemplateWithCustomPlainText } from '../shared/utils/template';
77
import { renderAsync } from './render-async';
88

99
type Import = typeof import('react-dom/server') & {
@@ -113,6 +113,26 @@ describe('renderAsync on the browser environment', () => {
113113
`);
114114
});
115115

116+
it('converts a React component with custom PlainText into HTML', async () => {
117+
const actualOutput = await renderAsync(<Template firstName="Jim" />);
118+
119+
expect(actualOutput).toMatchInlineSnapshot(
120+
`"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><link rel="preload" as="image" href="img/test.png"/><!--$--><h1>Welcome, <!-- -->Jim<!-- -->!</h1><img alt="test" src="img/test.png"/><p>Thanks for trying our product. We&#x27;re thrilled to have you on board!</p><!--/$-->"`,
121+
);
122+
});
123+
124+
it('converts a React component with custom PlainText into the custom PlainText', async () => {
125+
const actualOutput = await renderAsync(<TemplateWithCustomPlainText firstName="Jim" />, {
126+
plainText: true,
127+
});
128+
129+
expect(actualOutput).toMatchInlineSnapshot(`
130+
"HELLO, JIM!
131+
132+
Thanks for trying our product."
133+
`);
134+
});
135+
116136
it('converts to plain text and removes reserved ID', async () => {
117137
const actualOutput = await renderAsync(<Preview />, {
118138
plainText: true,

packages/render/src/browser/render-async.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ import type { Options } from '../shared/options';
44
import { plainTextSelectors } from '../shared/plain-text-selectors';
55
import { pretty } from '../shared/utils/pretty';
66
import { readStream } from './read-stream';
7+
import { OptionsContext } from '../shared/useRenderingOptions';
78

89
export const renderAsync = async (
910
element: React.ReactElement,
1011
options?: Options,
1112
) => {
12-
const suspendedElement = <Suspense>{element}</Suspense>;
13+
const suspendedElement = <Suspense>
14+
<OptionsContext.Provider value={options ?? {}}>{element}</OptionsContext.Provider>
15+
</Suspense>;
1316
const reactDOMServer = await import('react-dom/server');
1417

1518
let html!: string;

packages/render/src/browser/render-web.spec.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*/
44

55
import { Preview } from '../shared/utils/preview';
6-
import { Template } from '../shared/utils/template';
6+
import { Template, TemplateWithCustomPlainText } from '../shared/utils/template';
77
import { render } from './render';
88

99
type Import = typeof import('react-dom/server') & {
@@ -113,6 +113,26 @@ describe('render on the browser environment', () => {
113113
`);
114114
});
115115

116+
it('converts a React component with custom PlainText into HTML', async () => {
117+
const actualOutput = await render(<Template firstName="Jim" />);
118+
119+
expect(actualOutput).toMatchInlineSnapshot(
120+
`"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><link rel="preload" as="image" href="img/test.png"/><!--$--><h1>Welcome, <!-- -->Jim<!-- -->!</h1><img alt="test" src="img/test.png"/><p>Thanks for trying our product. We&#x27;re thrilled to have you on board!</p><!--/$-->"`,
121+
);
122+
});
123+
124+
it('converts a React component with custom PlainText into the custom PlainText', async () => {
125+
const actualOutput = await render(<TemplateWithCustomPlainText firstName="Jim" />, {
126+
plainText: true,
127+
});
128+
129+
expect(actualOutput).toMatchInlineSnapshot(`
130+
"HELLO, JIM!
131+
132+
Thanks for trying our product."
133+
`);
134+
});
135+
116136
it('converts to plain text and removes reserved ID', async () => {
117137
const actualOutput = await render(<Preview />, {
118138
plainText: true,

packages/render/src/browser/render.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
import type { Options } from '../shared/options';
88
import { plainTextSelectors } from '../shared/plain-text-selectors';
99
import { pretty } from '../shared/utils/pretty';
10+
import { OptionsContext } from '../shared/useRenderingOptions';
1011

1112
const decoder = new TextDecoder('utf-8');
1213

@@ -52,7 +53,9 @@ export const render = async (
5253
element: React.ReactElement,
5354
options?: Options,
5455
) => {
55-
const suspendedElement = <Suspense>{element}</Suspense>;
56+
const suspendedElement = <Suspense>
57+
<OptionsContext.Provider value={options ?? {}}>{element}</OptionsContext.Provider>
58+
</Suspense>;
5659
const reactDOMServer = await import('react-dom/server');
5760

5861
let html!: string;

packages/render/src/node/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ export * from './render-async';
33

44
export * from '../shared/options';
55
export * from '../shared/plain-text-selectors';
6+
7+
export { useRenderingOptions } from '../shared/useRenderingOptions'

packages/render/src/node/render-async-edge.spec.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import React from 'react';
66
import { Preview } from '../shared/utils/preview';
7-
import { Template } from '../shared/utils/template';
7+
import { Template, TemplateWithCustomPlainText } from '../shared/utils/template';
88
import { renderAsync } from './render-async';
99

1010
type Import = typeof import('react-dom/server') & {
@@ -94,6 +94,26 @@ describe('renderAsync on the edge', () => {
9494
`);
9595
});
9696

97+
it('converts a React component with custom PlainText into HTML', async () => {
98+
const actualOutput = await renderAsync(<Template firstName="Jim" />);
99+
100+
expect(actualOutput).toMatchInlineSnapshot(
101+
`"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><link rel="preload" as="image" href="img/test.png"/><!--$--><h1>Welcome, <!-- -->Jim<!-- -->!</h1><img alt="test" src="img/test.png"/><p>Thanks for trying our product. We&#x27;re thrilled to have you on board!</p><!--/$-->"`,
102+
);
103+
});
104+
105+
it('converts a React component with custom PlainText into the custom PlainText', async () => {
106+
const actualOutput = await renderAsync(<TemplateWithCustomPlainText firstName="Jim" />, {
107+
plainText: true,
108+
});
109+
110+
expect(actualOutput).toMatchInlineSnapshot(`
111+
"HELLO, JIM!
112+
113+
Thanks for trying our product."
114+
`);
115+
});
116+
97117
it('converts to plain text and removes reserved ID', async () => {
98118
const actualOutput = await renderAsync(<Preview />, {
99119
plainText: true,

packages/render/src/node/render-async-node.spec.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import { Suspense } from 'react';
66
import usePromise from 'react-promise-suspense';
77
import { Preview } from '../shared/utils/preview';
8-
import { Template } from '../shared/utils/template';
8+
import { Template, TemplateWithCustomPlainText } from '../shared/utils/template';
99
import { renderAsync } from './render-async';
1010

1111
type Import = typeof import('react-dom/server') & {
@@ -114,6 +114,26 @@ describe('renderAsync on node environments', () => {
114114
`);
115115
});
116116

117+
it('converts a React component with custom PlainText into HTML', async () => {
118+
const actualOutput = await renderAsync(<Template firstName="Jim" />);
119+
120+
expect(actualOutput).toMatchInlineSnapshot(
121+
`"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><link rel="preload" as="image" href="img/test.png"/><!--$--><h1>Welcome, <!-- -->Jim<!-- -->!</h1><img alt="test" src="img/test.png"/><p>Thanks for trying our product. We&#x27;re thrilled to have you on board!</p><!--/$-->"`,
122+
);
123+
});
124+
125+
it('converts a React component with custom PlainText into the custom PlainText', async () => {
126+
const actualOutput = await renderAsync(<TemplateWithCustomPlainText firstName="Jim" />, {
127+
plainText: true,
128+
});
129+
130+
expect(actualOutput).toMatchInlineSnapshot(`
131+
"HELLO, JIM!
132+
133+
Thanks for trying our product."
134+
`);
135+
});
136+
117137
it('converts to plain text and removes reserved ID', async () => {
118138
const actualOutput = await renderAsync(<Preview />, {
119139
plainText: true,

packages/render/src/node/render-async.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { Options } from '../shared/options';
44
import { plainTextSelectors } from '../shared/plain-text-selectors';
55
import { pretty } from '../shared/utils/pretty';
66
import { readStream } from './read-stream';
7+
import { OptionsContext } from '../shared/useRenderingOptions';
78

89
/**
910
* @deprecated use `render`
@@ -12,7 +13,9 @@ export const renderAsync = async (
1213
element: React.ReactElement,
1314
options?: Options,
1415
) => {
15-
const suspendedElement = <Suspense>{element}</Suspense>;
16+
const suspendedElement = <Suspense>
17+
<OptionsContext.Provider value={options ?? {}}>{element}</OptionsContext.Provider>
18+
</Suspense>;
1619
const reactDOMServer = await import('react-dom/server');
1720

1821
let html!: string;

packages/render/src/node/render-edge.spec.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import React from 'react';
66
import { Preview } from '../shared/utils/preview';
7-
import { Template } from '../shared/utils/template';
7+
import { Template, TemplateWithCustomPlainText } from '../shared/utils/template';
88
import { render } from './render';
99

1010
type Import = typeof import('react-dom/server') & {
@@ -103,6 +103,26 @@ describe('render on the edge', () => {
103103
`);
104104
});
105105

106+
it('converts a React component with custom PlainText into HTML', async () => {
107+
const actualOutput = await render(<Template firstName="Jim" />);
108+
109+
expect(actualOutput).toMatchInlineSnapshot(
110+
`"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><link rel="preload" as="image" href="img/test.png"/><!--$--><h1>Welcome, <!-- -->Jim<!-- -->!</h1><img alt="test" src="img/test.png"/><p>Thanks for trying our product. We&#x27;re thrilled to have you on board!</p><!--/$-->"`,
111+
);
112+
});
113+
114+
it('converts a React component with custom PlainText into the custom PlainText', async () => {
115+
const actualOutput = await render(<TemplateWithCustomPlainText firstName="Jim" />, {
116+
plainText: true,
117+
});
118+
119+
expect(actualOutput).toMatchInlineSnapshot(`
120+
"HELLO, JIM!
121+
122+
Thanks for trying our product."
123+
`);
124+
});
125+
106126
it('converts to plain text and removes reserved ID', async () => {
107127
const actualOutput = await render(<Preview />, {
108128
plainText: true,

packages/render/src/node/render-node.spec.tsx

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import { Suspense } from 'react';
66
import usePromise from 'react-promise-suspense';
77
import { Preview } from '../shared/utils/preview';
8-
import { Template } from '../shared/utils/template';
8+
import { Template, TemplateWithCustomPlainText } from '../shared/utils/template';
99
import { render } from './render';
1010

1111
type Import = typeof import('react-dom/server') & {
@@ -112,6 +112,14 @@ describe('render on node environments', () => {
112112
);
113113
});
114114

115+
it('converts a React component into HTML', async () => {
116+
const actualOutput = await render(<Template firstName="Jim" />);
117+
118+
expect(actualOutput).toMatchInlineSnapshot(
119+
`"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><link rel="preload" as="image" href="img/test.png"/><!--$--><h1>Welcome, <!-- -->Jim<!-- -->!</h1><img alt="test" src="img/test.png"/><p>Thanks for trying our product. We&#x27;re thrilled to have you on board!</p><!--/$-->"`,
120+
);
121+
});
122+
115123
it('converts a React component into PlainText', async () => {
116124
const actualOutput = await render(<Template firstName="Jim" />, {
117125
plainText: true,
@@ -124,6 +132,26 @@ describe('render on node environments', () => {
124132
`);
125133
});
126134

135+
it('converts a React component with custom PlainText into HTML', async () => {
136+
const actualOutput = await render(<Template firstName="Jim" />);
137+
138+
expect(actualOutput).toMatchInlineSnapshot(
139+
`"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><link rel="preload" as="image" href="img/test.png"/><!--$--><h1>Welcome, <!-- -->Jim<!-- -->!</h1><img alt="test" src="img/test.png"/><p>Thanks for trying our product. We&#x27;re thrilled to have you on board!</p><!--/$-->"`,
140+
);
141+
});
142+
143+
it('converts a React component with custom PlainText into the custom PlainText', async () => {
144+
const actualOutput = await render(<TemplateWithCustomPlainText firstName="Jim" />, {
145+
plainText: true,
146+
});
147+
148+
expect(actualOutput).toMatchInlineSnapshot(`
149+
"HELLO, JIM!
150+
151+
Thanks for trying our product."
152+
`);
153+
});
154+
127155
it('converts to plain text and removes reserved ID', async () => {
128156
const actualOutput = await render(<Preview />, {
129157
plainText: true,

packages/render/src/node/render.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ import type { Options } from '../shared/options';
44
import { plainTextSelectors } from '../shared/plain-text-selectors';
55
import { pretty } from '../shared/utils/pretty';
66
import { readStream } from './read-stream';
7+
import { OptionsContext } from '../shared/useRenderingOptions';
78

89
export const render = async (
910
element: React.ReactElement,
1011
options?: Options,
1112
) => {
12-
const suspendedElement = <Suspense>{element}</Suspense>;
13+
const suspendedElement = <Suspense>
14+
<OptionsContext.Provider value={options ?? {}}>{element}</OptionsContext.Provider>
15+
</Suspense>;
1316
const reactDOMServer = await import('react-dom/server');
1417

1518
let html!: string;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { createContext, useContext } from "react";
2+
import { Options } from "./options";
3+
4+
export const OptionsContext = createContext<Options>({});
5+
6+
export function useRenderingOptions() {
7+
const opts = useContext(OptionsContext);
8+
return opts;
9+
}

packages/render/src/shared/utils/template.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type * as React from 'react';
2+
import { useRenderingOptions } from '../useRenderingOptions';
23

34
interface TemplateProps {
45
firstName: string;
@@ -11,3 +12,24 @@ export const Template: React.FC<Readonly<TemplateProps>> = ({ firstName }) => (
1112
<p>Thanks for trying our product. We're thrilled to have you on board!</p>
1213
</>
1314
);
15+
16+
export const TemplateWithCustomPlainText: React.FC<Readonly<TemplateProps>> = ({ firstName }) => {
17+
const options = useRenderingOptions();
18+
19+
if (options.plainText)
20+
return (
21+
<>
22+
<h1>Hello, {firstName}!</h1>
23+
<img alt="test" src="img/test.png" />
24+
<p>Thanks for trying our product.</p>
25+
</>
26+
)
27+
28+
return (
29+
<>
30+
<h1>Welcome, {firstName}!</h1>
31+
<img alt="test" src="img/test.png" />
32+
<p>Thanks for trying our product. We're thrilled to have you on board!</p>
33+
</>
34+
)
35+
};

0 commit comments

Comments
 (0)