Skip to content

Commit e8047f2

Browse files
MartinCupelaoliverlazarnautov-anton
committed
feat: apply keepLineBreaksPlugin & htmlToTextPlugin plugins to text rendering by default (#2169)
Apply the remark plugins `keepLineBreaksPlugin`, `htmlToTextPlugin` as a part of the default message text parsing. - [X] merge #2170 - [X] include the plugins among the default remark plugins - [X] migration guide - see below - [X] remove docs section [Optional remark and rehype plugins](https://getstream.io/chat/docs/sdk/react/components/core-components/message_list/#optional-remark-and-rehype-plugins)](https://getstream.io/chat/docs/sdk/react/components/core-components/message_list/#overriding-defaults) - [X] upgrade Jest / install a new test runner (vitest) - new deps are not found with old Jest 26.6.3 - [X] include migration guide text in docs - [X] refactor [`customMarkDownRenderers` code in `renderText`](https://github.com/GetStream/stream-chat-react/blob/f0bc7ba2532760cabb1db01e685a35bd3b0b64c5/src/components/Message/renderText/renderText.tsx#L158) BREAKING CHANGE: apply the remark plugins `keepLineBreaksPlugin`, `htmlToTextPlugin` as a part of the default message text parsing, upgrade `unified` libraries --------- Co-authored-by: Oliver Lazoroski <[email protected]> Co-authored-by: Anton Arnautov <[email protected]>
1 parent 3dd8dab commit e8047f2

File tree

21 files changed

+1988
-1634
lines changed

21 files changed

+1988
-1634
lines changed

.babelrc.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// The content of babel.config.js copied here. This is due to the bug in jest - https://github.com/jestjs/jest/issues/11741
2+
// The bug has been resolved with jest 30 - https://github.com/jestjs/jest/commit/983274ac08c67d2a445e111b2dfaf81020f912b2
3+
module.exports = {
4+
env: {
5+
production: {
6+
presets: [
7+
[
8+
'@babel/env',
9+
{
10+
modules: false,
11+
},
12+
],
13+
],
14+
},
15+
test: {
16+
plugins: ['transform-es2015-modules-commonjs'],
17+
presets: [
18+
[
19+
'@babel/preset-env',
20+
{
21+
modules: 'commonjs',
22+
},
23+
],
24+
],
25+
},
26+
},
27+
ignore: ['src/@types/*'],
28+
plugins: [
29+
'@babel/plugin-proposal-class-properties',
30+
'@babel/plugin-transform-runtime',
31+
'babel-plugin-dynamic-import-node',
32+
],
33+
presets: ['@babel/preset-typescript', '@babel/env', '@babel/preset-react'],
34+
};

docusaurus/docs/React/components/core-components/message-list.mdx

Lines changed: 36 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,42 @@ const App = () => (
122122
);
123123
```
124124

125+
#### Custom element rendering
126+
127+
If you feel like the default output is sufficient, but you'd like to adjust how certain [ReactMarkdown components](https://github.com/remarkjs/react-markdown#appendix-b-components) look like (like `strong` element generated by typing \*\*strong\*\*) you can do so by passing down options to a third argument of the default `renderText` function:
128+
129+
:::note
130+
Types `mention` and `emoji` are special case component types generated by our SDK's custom rehype plugins.
131+
:::
132+
133+
```tsx
134+
import { renderText } from 'stream-chat-react';
135+
136+
const StrongComponent = ({ children }) => <b className='custom-strong-class-name'>{children}</b>;
137+
138+
const MentionComponent = ({ children, node: { mentionedUser } }) => (
139+
<a data-user-id={mentionedUser.id} href={`/user-profile/${mentionedUser.id}`}>
140+
{children}
141+
</a>
142+
);
143+
144+
const App = () => (
145+
<Chat client={client}>
146+
<Channel>
147+
<Window>
148+
<MessageList
149+
renderText={(text, mentionedUsers) =>
150+
renderText(text, mentionedUsers, {
151+
customMarkDownRenderers: { strong: StrongComponent, mention: MentionComponent },
152+
})
153+
}
154+
/>
155+
</Window>
156+
</Channel>
157+
</Chat>
158+
);
159+
```
160+
125161
#### Custom remark and rehype plugins
126162

127163
If you would like to extend the array of plugins used to parse the markdown, you can provide your own lists of remark resp. rehype plugins. The logic that determines what plugins are used and in which order can be specified in custom `getRehypePlugins` and `getRemarkPlugins` functions. These receive the default array of rehype and remark plugins for further customization. Both custom functions ought to be passed to the third `renderText()` parameter. An example follows:
@@ -182,79 +218,6 @@ const CustomMessageList = () => (
182218
);
183219
```
184220

185-
#### Optional remark and rehype plugins
186-
187-
The SDK provides the following plugins that are not applied to the text parsing by the default `renderText()` implementation. However, these can be included by simply overriding the defaults with `getRemarkPlugins` and `getRehypePlugins` parameters as described in [the section about custom plugins](#custom-remark-and-rehype-plugins).
188-
189-
Currently, there are the following optional remark plugins available in the SDK:
190-
191-
- `htmlToTextPlugin` - keeps the HTML tags in the resulting text string.
192-
- `keepLineBreaksPlugin` - replaces empty lines in text with line breaks ([according to the CommonMark Markdown specification](https://spec.commonmark.org/0.30/#hard-line-breaks), the empty lines - meaning newline characters `\n` - are not transformed to `<br/>` elements).
193-
194-
These can be plugged in as follows:
195-
196-
```tsx
197-
import { UserResponse } from 'stream-chat';
198-
import {
199-
htmlToTextPlugin,
200-
keepLineBreaksPlugin,
201-
MessageList,
202-
MessageListProps,
203-
renderText,
204-
RenderTextPluginConfigurator,
205-
} from 'stream-chat-react';
206-
207-
const getRemarkPlugins: RenderTextPluginConfigurator = (plugins) => [
208-
htmlToTextPlugin,
209-
keepLineBreaksPlugin,
210-
...plugins,
211-
];
212-
213-
function customRenderText(text?: string, mentionedUsers?: UserResponse[]) {
214-
return renderText(text, mentionedUsers, { getRemarkPlugins });
215-
}
216-
217-
export const CustomMessageList = (props: MessageListProps) => (
218-
<MessageList {...props} renderText={customRenderText} />
219-
);
220-
```
221-
222-
#### Custom element rendering
223-
224-
If you feel like the default output is sufficient, but you'd like to adjust how certain [ReactMarkdown components](https://github.com/remarkjs/react-markdown#appendix-b-components) look like (like `strong` element generated by typing \*\*strong\*\*) you can do so by passing down options to a third argument of the default `renderText` function:
225-
226-
:::note
227-
Types `mention` and `emoji` are special case component types generated by our SDK's custom rehype plugins.
228-
:::
229-
230-
```tsx
231-
import { renderText } from 'stream-chat-react';
232-
233-
const StrongComponent = ({ children }) => <b className='custom-strong-class-name'>{children}</b>;
234-
235-
const MentionComponent = ({ children, node: { mentionedUser } }) => (
236-
<a data-user-id={mentionedUser.id} href={`/user-profile/${mentionedUser.id}`}>
237-
{children}
238-
</a>
239-
);
240-
241-
const App = () => (
242-
<Chat client={client}>
243-
<Channel>
244-
<Window>
245-
<MessageList
246-
renderText={(text, mentionedUsers) =>
247-
renderText(text, mentionedUsers, {
248-
customMarkDownRenderers: { strong: StrongComponent, mention: MentionComponent },
249-
})
250-
}
251-
/>
252-
</Window>
253-
</Channel>
254-
</Chat>
255-
);
256-
```
257-
258221
## Props
259222

260223
### additionalMessageInputProps
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
---
2+
id: message-text-rendering-v11
3+
sidebar_position: 3
4+
title: Message text rendering 11.0.0
5+
keywords: [migration guide, upgrade, message, text rendering, breaking changes, v11]
6+
---
7+
8+
Optional remark plugins `htmlToTextPlugin`, `keepLineBreaksPlugin` introduced with [[email protected]](https://github.com/GetStream/stream-chat-react/releases/tag/v10.19.0) are now included among the default remark plugins. That means that in the messages that originally contained a sequence of multiple newline characters `\n`, these will be replaced with line break elements `<br/>`. The number of line breaks equals count of newline characters minus 1.
9+
The dependencies used to parse the markdown with [`renderText()` function](../../components/core-components/message_list/#rendering-message-text-with-rendertext-function) have been upgraded as a result of that, the following changes had to be performed in stream-chat-react library:
10+
11+
### `ReactMarkdownProps` dropped from `customMarkDownRenderers`
12+
13+
`RenderTextOptions.customMarkDownRenderers`- a mapping of element name and corresponding React component to be rendered. The components are no longer accepting `ReactMarkdownProps`
14+
15+
### User mention renderer props change
16+
17+
The `RenderTextOptions.customMarkDownRenderers.mention` props have been reduced. From now on, only `children` and `node` are passed to the component. And so now `mention` renderer props look as follows:
18+
19+
```ts
20+
import { PropsWithChildren } from 'react';
21+
import type { UserResponse } from 'stream-chat';
22+
import type { DefaultStreamChatGenerics } from 'stream-chat-react';
23+
24+
type MentionProps<
25+
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
26+
> = PropsWithChildren<{
27+
node: {
28+
mentionedUser: UserResponse<StreamChatGenerics>;
29+
};
30+
}>;
31+
```
32+
33+
### Adjust custom rehype or remark plugins
34+
35+
If you have implemented your own rehype or remark plugin using `visit` function from the library `unist-util-visit` beware that the `index` and `parent` arguments of the `Visitor` function cannot be `null` but `undefined` instead. You should be notified by Typescript about this and should adjust the type checks accordingly.
36+
37+
If you would like to prevent applying plugins `htmlToTextPlugin`, `keepLineBreaksPlugin`, you can customize your `renderText()` by overriding the remark plugins. The example below will keep the plugin `remarkGfm` and exclude the rest:
38+
39+
```tsx
40+
import remarkGfm from 'remark-gfm';
41+
import { renderText, RenderTextPluginConfigurator } from 'stream-chat-react';
42+
43+
44+
const getRemarkPlugins: RenderTextPluginConfigurator = () => {
45+
return [[remarkGfm, { singleTilde: false }]];
46+
};
47+
48+
const customRenderText = (text, mentionedUsers) =>
49+
renderText(text, mentionedUsers, {
50+
getRemarkPlugins
51+
});
52+
const CustomMessageList = () => (
53+
<MessageList renderText={customRenderText}/>
54+
);
55+
```

jest.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ module.exports = {
1212
},
1313
preset: 'ts-jest',
1414
setupFiles: ['core-js'],
15+
setupFilesAfterEnv: ['<rootDir>/jest.env.setup.js'],
16+
testEnvironment: 'jsdom',
1517
testPathIgnorePatterns: ['/node_modules/', '/examples/', '__snapshots__', '/e2e/'],
1618
testRegex: [
1719
/**

jest.env.setup.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/* eslint-disable no-undef */
2+
const crypto = require('crypto');
3+
4+
Object.defineProperty(globalThis, 'crypto', {
5+
value: {
6+
getRandomValues: (arr) => crypto.randomBytes(arr.length),
7+
},
8+
});

package.json

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"dayjs": "^1.10.4",
3636
"emoji-mart": "3.0.1",
3737
"emoji-regex": "^9.2.0",
38-
"hast-util-find-and-replace": "^4.1.2",
38+
"hast-util-find-and-replace": "^5.0.1",
3939
"i18next": "^21.6.14",
4040
"isomorphic-ws": "^4.0.1",
4141
"linkifyjs": "^4.1.0",
@@ -49,16 +49,16 @@
4949
"react-fast-compare": "^3.2.2",
5050
"react-image-gallery": "1.2.12",
5151
"react-is": "^18.1.0",
52-
"react-markdown": "^8.0.7",
52+
"react-markdown": "^9.0.0",
5353
"react-player": "2.10.1",
5454
"react-popper": "^2.3.0",
5555
"react-textarea-autosize": "^8.3.0",
5656
"react-virtuoso": "^2.16.5",
57-
"remark-gfm": "^3.0.1",
57+
"remark-gfm": "^4.0.0",
5858
"textarea-caret": "^3.1.0",
5959
"tslib": "^2.6.2",
6060
"unist-builder": "^3.0.0",
61-
"unist-util-visit": "^4.1.1"
61+
"unist-util-visit": "^5.0.0"
6262
},
6363
"optionalDependencies": {
6464
"@stream-io/transliterate": "^1.5.5",
@@ -82,7 +82,7 @@
8282
"@babel/plugin-proposal-class-properties": "^7.12.1",
8383
"@babel/plugin-transform-runtime": "^7.12.1",
8484
"@babel/preset-env": "^7.12.7",
85-
"@babel/preset-react": "^7.12.7",
85+
"@babel/preset-react": "^7.23.3",
8686
"@babel/preset-typescript": "^7.12.7",
8787
"@commitlint/cli": "^16.2.3",
8888
"@commitlint/config-conventional": "^16.2.1",
@@ -98,13 +98,14 @@
9898
"@semantic-release/changelog": "^6.0.2",
9999
"@semantic-release/git": "^10.0.1",
100100
"@stream-io/rollup-plugin-node-builtins": "^2.1.5",
101-
"@testing-library/jest-dom": "^5.16.4",
101+
"@testing-library/jest-dom": "^6.1.4",
102102
"@testing-library/react": "^13.1.1",
103103
"@testing-library/react-hooks": "^8.0.0",
104104
"@types/deep-equal": "^1.0.1",
105105
"@types/dotenv": "^8.2.0",
106106
"@types/emoji-mart": "^3.0.9",
107107
"@types/hast": "^2.3.4",
108+
"@types/jsdom": "^21.1.5",
108109
"@types/linkifyjs": "^2.1.3",
109110
"@types/lodash.debounce": "^4.0.7",
110111
"@types/lodash.throttle": "^4.1.7",
@@ -120,7 +121,7 @@
120121
"@typescript-eslint/parser": "4.27.0",
121122
"autoprefixer": "^10.0.3",
122123
"babel-eslint": "^10.1.0",
123-
"babel-jest": "^26.3.0",
124+
"babel-jest": "^28.1.3",
124125
"babel-loader": "8.2.2",
125126
"babel-plugin-module-resolver": "^4.1.0",
126127
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
@@ -152,8 +153,9 @@
152153
"file-loader": "^6.2.0",
153154
"husky": "^4.3.0",
154155
"i18next-parser": "^6.0.0",
155-
"jest": "^26.6.3",
156-
"jest-axe": "^6.0.0",
156+
"jest": "^28.1.3",
157+
"jest-axe": "^8.0.0",
158+
"jest-environment-jsdom": "^28.1.3",
157159
"moment-timezone": "^0.5.43",
158160
"postcss": "^8.1.10",
159161
"postcss-loader": "^4.1.0",
@@ -172,7 +174,7 @@
172174
"semantic-release": "^19.0.5",
173175
"stream-chat": "^8.13.1",
174176
"style-loader": "^2.0.0",
175-
"ts-jest": "^26.5.1",
177+
"ts-jest": "^28.0.8",
176178
"typescript": "^4.7.4",
177179
"url-loader": "^4.1.1",
178180
"webpack": "4.44.2",

src/components/Message/renderText/Emoji.tsx

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/components/Message/renderText/Mention.tsx

Lines changed: 0 additions & 31 deletions
This file was deleted.

0 commit comments

Comments
 (0)