Skip to content

Commit e71bb12

Browse files
Separate renderText from MessageList
1 parent 82d2c11 commit e71bb12

File tree

4 files changed

+165
-165
lines changed

4 files changed

+165
-165
lines changed

Diff for: docusaurus/docs/React/components/core-components/message-list.mdx

+8-164
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ By default, the `MessageList` loads the most recent 25 messages held in the `cha
1515
from the Stream Chat API and loaded into the DOM on scrolling up the list. The currently loaded messages are held in
1616
the `ChannelStateContext` and can be referenced with our custom hook.
1717

18-
```jsx
18+
```tsx
1919
const { messages } = useChannelStateContext();
2020
```
2121

@@ -31,7 +31,7 @@ value drawn from the `ChannelStateContext`.
3131

3232
**Example 1** - without `messages` prop
3333

34-
```jsx
34+
```tsx
3535
<Chat client={client}>
3636
<ChannelList />
3737
<Channel>
@@ -43,7 +43,7 @@ value drawn from the `ChannelStateContext`.
4343

4444
**Example 2** - with `messages` prop
4545

46-
```jsx
46+
```tsx
4747
const customMessages = [
4848
// array of messages
4949
];
@@ -61,163 +61,7 @@ const customMessages = [
6161

6262
The `MessageList` internally creates a mapping of message id to a style group. There are 4 style groups - ` 'middle' | 'top' | 'bottom' | 'single'`. Later these group style tags are incorporated into the class of the `<li/>` element that wraps the `Message` component. This allows us to style messages by their position in the message group. An example of such class is `str-chat__li str-chat__li--bottom`.
6363

64-
## Rendering message text with `renderText` function
65-
66-
### Default behaviour
67-
68-
The default [`renderText`](#render-text) function parses a markdown string and outputs a `ReactElement`. Under the hood, the output is generated by the `ReactMarkdown` component from [react-markdown library](https://github.com/remarkjs/react-markdown). The component transforms the markdown to `ReactElement` by using [`remark` parser](https://github.com/remarkjs/remark/tree/main) and [`remark`](https://github.com/remarkjs/remark/blob/main/doc/plugins.md) and [`rehype`](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md) plugins.
69-
70-
The default `remark` plugins used by SDK are:
71-
72-
1. [`remark-gfm`](https://github.com/remarkjs/remark-gfm) - a third party plugin to add GitHub-like markdown support
73-
74-
The default `rehype` plugins (both specific to this SDK) are:
75-
1. plugin to render user mentions
76-
2. plugin to render emojis
77-
78-
### Overriding defaults
79-
80-
#### Custom `renderText` function
81-
82-
If you don't want your chat implementation to support markdown syntax by default you can override the default behaviour by creating a custom `renderText` function which returns a React node and passing it down to the `MessageList` or `MessageSimple` component via `renderText` property.
83-
84-
For this particular example we'll create a very primitive one which takes the message text passed down to it as a first argument and returns it wrapped in `span` element:
85-
86-
```tsx
87-
const customRenderText = (text) => {
88-
return <span>{text}</span>;
89-
};
90-
91-
const App = () => (
92-
<Chat client={client}>
93-
<Channel>
94-
<Window>
95-
<MessageList renderText={customRenderText} />
96-
</Window>
97-
</Channel>
98-
</Chat>
99-
);
100-
```
101-
102-
Here's also an example with `VirtualizedMessageList` which currently does not accept `renderText` directly:
103-
104-
```tsx
105-
import { MessageSimple } from 'stream-chat-react';
106-
107-
const customRenderText = (text) => {
108-
return <span>{text}</span>;
109-
};
110-
111-
const CustomMessage = (props) => <MessageSimple {...props} renderText={customRenderText} />;
112-
113-
const App = () => (
114-
<Chat client={client}>
115-
<Channel>
116-
<Window>
117-
<VirtualizedMessageList Message={CustomMessage} />
118-
</Window>
119-
</Channel>
120-
</Chat>
121-
);
122-
```
123-
124-
#### Custom element rendering
125-
126-
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:
127-
128-
:::note
129-
Types `mention` and `emoji` are special case component types generated by our SDK's custom rehype plugins.
130-
:::
131-
132-
```tsx
133-
import { renderText } from 'stream-chat-react';
134-
135-
const StrongComponent = ({ children }) => <b className='custom-strong-class-name'>{children}</b>;
136-
137-
const MentionComponent = ({ children, node: { mentionedUser } }) => (
138-
<a data-user-id={mentionedUser.id} href={`/user-profile/${mentionedUser.id}`}>
139-
{children}
140-
</a>
141-
);
142-
143-
const App = () => (
144-
<Chat client={client}>
145-
<Channel>
146-
<Window>
147-
<MessageList
148-
renderText={(text, mentionedUsers) =>
149-
renderText(text, mentionedUsers, {
150-
customMarkDownRenderers: { strong: StrongComponent, mention: MentionComponent },
151-
})
152-
}
153-
/>
154-
</Window>
155-
</Channel>
156-
</Chat>
157-
);
158-
```
159-
160-
#### Custom remark and rehype plugins
161-
162-
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:
163-
164-
:::note
165-
It is important to understand what constitutes a rehype or remark plugin. A good start is to learn about the library called [`react-remark`](https://github.com/remarkjs/react-remark) which is used under the hood in our `renderText()` function.
166-
:::
167-
168-
169-
```tsx
170-
import { renderText, RenderTextPluginConfigurator } from 'stream-chat-react';
171-
import {customRehypePlugin} from './rehypePlugins';
172-
import {customRemarkPlugin} from './remarkPlugins';
173-
174-
const getRehypePlugins: RenderTextPluginConfigurator = (plugins) => {
175-
return [customRehypePlugin, ...plugins];
176-
}
177-
const getRemarkPlugins: RenderTextPluginConfigurator = (plugins) => {
178-
return [customRemarkPlugin, ...plugins];
179-
}
180-
181-
const customRenderText = (text, mentionedUsers) =>
182-
renderText(text, mentionedUsers, {
183-
getRehypePlugins,
184-
getRemarkPlugins
185-
});
186-
187-
const CustomMessageList = () => (
188-
<MessageList renderText={customRenderText}/>
189-
);
190-
```
191-
192-
It is also possible to define your custom set of allowed tag names for the elements rendered from the parsed markdown. To perform the tree transformations, you will need to use libraries like [`unist-builder`](https://github.com/syntax-tree/unist-builder) to build the trees and [`unist-util-visit`](https://github.com/syntax-tree/unist-util-visit-parents) or [`hast-util-find-and-replace`](https://github.com/syntax-tree/hast-util-find-and-replace) to traverse the tree:
193-
194-
```tsx
195-
import { findAndReplace } from 'hast-util-find-and-replace';
196-
import { u } from 'unist-builder';
197-
import { defaultAllowedTagNames, renderText, RenderTextPluginConfigurator } from 'stream-chat-react';
198-
199-
// wraps every letter b in <xxx></xxx> tags
200-
const customTagName = 'xxx';
201-
const replace = (match) => u('element', { tagName: customTagName }, [u('text', match)]);
202-
const customRehypePlugin = () => (tree) => findAndReplace(tree, /b/, replace);
203-
204-
const getRehypePlugins: RenderTextPluginConfigurator = (plugins) => {
205-
return [customRehypePlugin, ...plugins];
206-
}
207-
208-
209-
const customRenderText = (text, mentionedUsers) =>
210-
renderText(text, mentionedUsers, {
211-
allowedTagNames: [...defaultAllowedTagNames, customTagName],
212-
getRehypePlugins,
213-
});
214-
215-
const CustomMessageList = () => (
216-
<MessageList renderText={customRenderText}/>
217-
);
218-
```
219-
220-
#### Custom message list rendering
64+
## Custom message list rendering
22165

22266
You can completely change the way the message list is rendered by providing a custom `renderMessages` function. This function takes all the messages fetched so far (along with some additional data) and returns an array of React elements to render. By overriding the default behavior, you can add custom elements to the message list, change the way messages are grouped, add custom separators between messages, etc.
22367

@@ -228,13 +72,13 @@ In this example, we use the default implementation for rendering a message list,
22872
```tsx
22973
const customRenderMessages: MessageRenderer<StreamChatGenerics> = (options) => {
23074
const elements = defaultRenderMessages(options);
75+
23176
elements.push(<li key='caught-up'>You're all caught up!</li>);
77+
23278
return elements;
23379
};
23480

235-
const CustomMessageList = () => (
236-
<MessageList renderMessages={customRenderMessages}/>
237-
);
81+
const CustomMessageList = () => <MessageList renderMessages={customRenderMessages} />;
23882
```
23983

24084
Make sure that the elements you return have `key`, as they will be rendered as an array. It's also a good idea to wrap each element with `<li>` to keep your markup semantically correct.
@@ -620,7 +464,7 @@ The floating notification informing about unread messages will be shown when the
620464
is shown only when viewing unread messages.
621465

622466
| Type | Default |
623-
|---------|---------|
467+
| ------- | ------- |
624468
| boolean | false |
625469

626470
### threadList
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
---
2+
id: render-text
3+
title: renderText function
4+
---
5+
6+
The `renderText` function is a core piece of functionality, which handles how the text of our message is going to be formatted/look like. The default [`renderText`](#render-text) function parses a markdown string and outputs a `ReactElement`. Under the hood, the output is generated by the `ReactMarkdown` component from [react-markdown library](https://github.com/remarkjs/react-markdown). The component transforms the markdown to `ReactElement` by using [`remark` parser](https://github.com/remarkjs/remark/tree/main) and [`remark`](https://github.com/remarkjs/remark/blob/main/doc/plugins.md) and [`rehype`](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md) plugins.
7+
8+
The default `remark` plugins used by SDK are:
9+
10+
1. [`remark-gfm`](https://github.com/remarkjs/remark-gfm) - a third party plugin to add GitHub-like markdown support
11+
12+
The default `rehype` plugins (both specific to this SDK) are:
13+
14+
1. plugin to render user mentions
15+
2. plugin to render emojis
16+
17+
### Overriding defaults
18+
19+
#### Custom `renderText` function
20+
21+
If you don't want your chat implementation to support markdown syntax by default you can override the default behaviour by creating a custom `renderText` function which returns a React node and passing it down to the `MessageList` or `MessageSimple` component via `renderText` property.
22+
23+
For this particular example we'll create a very primitive one which takes the message text passed down to it as a first argument and returns it wrapped in `span` element:
24+
25+
```tsx
26+
const customRenderText = (text) => {
27+
return <span>{text}</span>;
28+
};
29+
30+
const App = () => (
31+
<Chat client={client}>
32+
<Channel>
33+
<Window>
34+
<MessageList renderText={customRenderText} />
35+
</Window>
36+
</Channel>
37+
</Chat>
38+
);
39+
```
40+
41+
Here's also an example with `VirtualizedMessageList` which currently does not accept `renderText` directly:
42+
43+
```tsx
44+
import { MessageSimple } from 'stream-chat-react';
45+
46+
const customRenderText = (text) => {
47+
return <span>{text}</span>;
48+
};
49+
50+
const CustomMessage = (props) => <MessageSimple {...props} renderText={customRenderText} />;
51+
52+
const App = () => (
53+
<Chat client={client}>
54+
<Channel>
55+
<Window>
56+
<VirtualizedMessageList Message={CustomMessage} />
57+
</Window>
58+
</Channel>
59+
</Chat>
60+
);
61+
```
62+
63+
#### Custom element rendering
64+
65+
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:
66+
67+
:::note
68+
Types `mention` and `emoji` are special case component types generated by our SDK's custom rehype plugins.
69+
:::
70+
71+
```tsx
72+
import { renderText } from 'stream-chat-react';
73+
74+
const StrongComponent = ({ children }) => <b className='custom-strong-class-name'>{children}</b>;
75+
76+
const MentionComponent = ({ children, node: { mentionedUser } }) => (
77+
<a data-user-id={mentionedUser.id} href={`/user-profile/${mentionedUser.id}`}>
78+
{children}
79+
</a>
80+
);
81+
82+
const App = () => (
83+
<Chat client={client}>
84+
<Channel>
85+
<Window>
86+
<MessageList
87+
renderText={(text, mentionedUsers) =>
88+
renderText(text, mentionedUsers, {
89+
customMarkDownRenderers: { strong: StrongComponent, mention: MentionComponent },
90+
})
91+
}
92+
/>
93+
</Window>
94+
</Channel>
95+
</Chat>
96+
);
97+
```
98+
99+
#### Custom remark and rehype plugins
100+
101+
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:
102+
103+
:::note
104+
It is important to understand what constitutes a rehype or remark plugin. A good start is to learn about the library called [`react-remark`](https://github.com/remarkjs/react-remark) which is used under the hood in our `renderText()` function.
105+
:::
106+
107+
```tsx
108+
import { renderText, RenderTextPluginConfigurator } from 'stream-chat-react';
109+
import { customRehypePlugin } from './rehypePlugins';
110+
import { customRemarkPlugin } from './remarkPlugins';
111+
112+
const getRehypePlugins: RenderTextPluginConfigurator = (plugins) => {
113+
return [customRehypePlugin, ...plugins];
114+
};
115+
const getRemarkPlugins: RenderTextPluginConfigurator = (plugins) => {
116+
return [customRemarkPlugin, ...plugins];
117+
};
118+
119+
const customRenderText = (text, mentionedUsers) =>
120+
renderText(text, mentionedUsers, {
121+
getRehypePlugins,
122+
getRemarkPlugins,
123+
});
124+
125+
const CustomMessageList = () => <MessageList renderText={customRenderText} />;
126+
```
127+
128+
It is also possible to define your custom set of allowed tag names for the elements rendered from the parsed markdown. To perform the tree transformations, you will need to use libraries like [`unist-builder`](https://github.com/syntax-tree/unist-builder) to build the trees and [`unist-util-visit`](https://github.com/syntax-tree/unist-util-visit-parents) or [`hast-util-find-and-replace`](https://github.com/syntax-tree/hast-util-find-and-replace) to traverse the tree:
129+
130+
```tsx
131+
import { findAndReplace } from 'hast-util-find-and-replace';
132+
import { u } from 'unist-builder';
133+
import {
134+
defaultAllowedTagNames,
135+
renderText,
136+
RenderTextPluginConfigurator,
137+
} from 'stream-chat-react';
138+
139+
// wraps every letter b in <xxx></xxx> tags
140+
const customTagName = 'xxx';
141+
const replace = (match) => u('element', { tagName: customTagName }, [u('text', match)]);
142+
const customRehypePlugin = () => (tree) => findAndReplace(tree, /b/, replace);
143+
144+
const getRehypePlugins: RenderTextPluginConfigurator = (plugins) => {
145+
return [customRehypePlugin, ...plugins];
146+
};
147+
148+
const customRenderText = (text, mentionedUsers) =>
149+
renderText(text, mentionedUsers, {
150+
allowedTagNames: [...defaultAllowedTagNames, customTagName],
151+
getRehypePlugins,
152+
});
153+
154+
const CustomMessageList = () => <MessageList renderText={customRenderText} />;
155+
```

Diff for: docusaurus/docs/React/release-guides/upgrade-to-v11.mdx

+1-1
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ const doSendMessageRequest: ChannelProps['doSendMessageRequest'] = async (
487487
## Message text rendering
488488

489489
Optional remark plugins `htmlToTextPlugin`, `keepLineBreaksPlugin` introduced with [stream-chat-react@v10.19.0](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.
490-
The dependencies used to parse the markdown with [`renderText()` function](../components/core-components/message-list.mdx#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:
490+
The dependencies used to parse the markdown with [`renderText` function](../components/message-components/render-text.mdx) have been upgraded as a result of that, the following changes had to be performed in stream-chat-react library:
491491

492492
### `ReactMarkdownProps` dropped from `customMarkDownRenderers`
493493

Diff for: docusaurus/sidebars-react.json

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"components/contexts/message_bounce_context",
4040
"hooks/message_hooks",
4141
"components/message-components/message_ui",
42+
"components/message-components/render-text",
4243
"components/message-components/ui-components",
4344
"components/utility-components/avatar",
4445
"components/utility-components/base-image",

0 commit comments

Comments
 (0)