Skip to content

Commit b356b94

Browse files
author
Sravan S
authored
feat: replace renderToString(react-dom) with custom fn (#449)
Replace renderToString from react-dom/server with custom function This function was creating issue in customers with cra@4 & react@17 fixes: https://sendbird.atlassian.net/browse/UIKIT-3403
1 parent db19e13 commit b356b94

File tree

7 files changed

+50
-32
lines changed

7 files changed

+50
-32
lines changed

src/ui/MentionUserLabel/__tests__/__snapshots__/MentionUserLabel.spec.js.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ exports[`ui/MentionUserLabel should do a snapshot test of the MentionUserLabel D
55
<span
66
class="sendbird-mention-user-label undefined"
77
contenteditable="false"
8+
data-sb-mention="true"
89
/>
910
</DocumentFragment>
1011
`;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import renderToString from "../renderToString";
2+
3+
describe("ui/MentionUserLabel/renderToString", () => {
4+
it("should render userId and nickname as expected", () => {
5+
const userId = "me";
6+
const nickname = "nickname";
7+
const expected = `<span contenteditable="false" class="sendbird-mention-user-label" data-sb-mention="true" data-userid="me">nickname</span>`;
8+
const result = renderToString({ userId, nickname });
9+
expect(result).toEqual(expected);
10+
});
11+
});

src/ui/MentionUserLabel/consts.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const MENTION_USER_LABEL_CLASSNAME = 'sendbird-mention-user-label';

src/ui/MentionUserLabel/index.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import React from 'react';
21
import './index.scss';
32

3+
import React from 'react';
4+
import { MENTION_USER_LABEL_CLASSNAME } from './consts';
5+
46
interface MentionUserLabelProps {
57
className?: string
68
children?: string;
@@ -18,9 +20,10 @@ export default function MentionUserLabel({
1820
}: MentionUserLabelProps): JSX.Element {
1921
return (
2022
<span
21-
className={`sendbird-mention-user-label ${className} ${isReverse ? 'reverse' : ''} ${color}`}
23+
className={`${MENTION_USER_LABEL_CLASSNAME} ${className} ${isReverse ? 'reverse' : ''} ${color}`}
2224
contentEditable={false}
2325
data-userid={userId}
26+
data-sb-mention={true}
2427
>
2528
{children}
2629
</span>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// cretes a sanitized string from a mention user label
2+
import DOMPurify from 'dompurify';
3+
import { MENTION_USER_LABEL_CLASSNAME } from './consts';
4+
5+
type renderToStringParams = {
6+
userId: string;
7+
nickname: string;
8+
};
9+
10+
export default function renderToString({ userId, nickname }: renderToStringParams): string {
11+
// donot change this template, it wont work
12+
const el = `<span data-userid="${userId}" data-sb-mention="true" class="${MENTION_USER_LABEL_CLASSNAME}">${nickname}</span>`;
13+
const purifier = DOMPurify(window);
14+
const sanitized_ = purifier.sanitize(el);
15+
const token = sanitized_.split(' ');
16+
const [spanTag, ...rest] = token;
17+
// we do this because DOMPurify removes the contenteditable attribute
18+
const sanitized = [spanTag, 'contenteditable="false"', ...rest].join(' ');
19+
return sanitized;
20+
}

src/ui/MessageInput/hooks/usePaste/insertTemplate.tsx renamed to src/ui/MessageInput/hooks/usePaste/insertTemplate.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,12 @@
1-
import React from 'react';
2-
import { renderToString } from 'react-dom/server';
3-
41
import { Word } from './types';
52
import { sanitizeString } from '../../utils';
6-
import MentionUserLabel from '../../../MentionUserLabel';
3+
import renderMentionLabelToString from '../../../MentionUserLabel/renderToString';
74

85
export function inserTemplateToDOM(templateList: Word[]): void {
96
const nodes = templateList.map((template) => {
107
const { text, userId } = template;
118
if (userId) {
12-
return (
13-
renderToString(
14-
<>
15-
<MentionUserLabel userId={userId}>
16-
{text}
17-
</MentionUserLabel>
18-
</>
19-
)
20-
);
9+
return renderMentionLabelToString({ userId, nickname: text });
2110
}
2211
return sanitizeString(text);
2312
})

src/ui/MessageInput/index.jsx

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import React, {
66
useCallback,
77
useContext,
88
} from 'react';
9-
import { renderToString } from 'react-dom/server';
109
import PropTypes from 'prop-types';
1110

1211
import './index.scss';
@@ -15,7 +14,7 @@ import { MessageInputKeys, NodeNames, NodeTypes } from './const';
1514
import { USER_MENTION_TEMP_CHAR } from '../../smart-components/Channel/context/const';
1615
import IconButton from '../IconButton';
1716
import Button, { ButtonTypes, ButtonSizes } from '../Button';
18-
import MentionUserLabel from '../MentionUserLabel';
17+
import renderMentionLabelToString from '../MentionUserLabel/renderToString';
1918
import Icon, { IconTypes, IconColors } from '../Icon';
2019
import Label, { LabelTypography, LabelColors } from '../Label';
2120
import { LocalizationContext } from '../../lib/LocalizationContext';
@@ -161,16 +160,11 @@ const MessageInput = React.forwardRef((props, ref) => {
161160
convertWordToStringObj(word, mentionedUsers).map((stringObj) => {
162161
const { type, value, userId } = stringObj;
163162
if (type === StringObjType.mention && mentionedUsers.some((user) => user?.userId === userId)) {
164-
return renderToString(
165-
<MentionUserLabel userId={userId}>
166-
{
167-
`${USER_MENTION_TEMP_CHAR}${mentionedUsers.find((user) => user?.userId === userId)?.nickname
168-
|| value
169-
|| stringSet.MENTION_NAME__NO_NAME
170-
}`
171-
}
172-
</MentionUserLabel>,
173-
);
163+
const nickname = `${USER_MENTION_TEMP_CHAR}${mentionedUsers.find((user) => user?.userId === userId)?.nickname
164+
|| value
165+
|| stringSet.MENTION_NAME__NO_NAME
166+
}`
167+
return renderMentionLabelToString({ userId, nickname });
174168
}
175169
return sanitizeString(value);
176170
}).join('')
@@ -220,11 +214,10 @@ const MessageInput = React.forwardRef((props, ref) => {
220214
const backTextNode = document?.createTextNode(
221215
`\u00A0${childNodes[endNodeIndex]?.textContent.slice(endOffsetIndex)}`,
222216
);
223-
const mentionLabel = renderToString(
224-
<MentionUserLabel userId={mentionSelectedUser?.userId}>
225-
{`${USER_MENTION_TEMP_CHAR}${mentionSelectedUser?.nickname || stringSet.MENTION_NAME__NO_NAME}`}
226-
</MentionUserLabel>,
227-
);
217+
const mentionLabel = renderMentionLabelToString({
218+
userId: mentionSelectedUser?.userId,
219+
nickname: `${USER_MENTION_TEMP_CHAR}${mentionSelectedUser?.nickname || stringSet.MENTION_NAME__NO_NAME}`,
220+
});
228221
const div = document.createElement('div');
229222
div.innerHTML = mentionLabel;
230223
const newNodes = [

0 commit comments

Comments
 (0)