Unverified Commit 692c44ea authored by Harrison Healey's avatar Harrison Healey Committed by GitHub

Moved messageHtmlToComponent into its own file (#1196)

parent da454ba2
......@@ -3,12 +3,12 @@
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {isChannelMuted} from 'mattermost-redux/utils/channel_utils';
import {isSystemMessage} from 'mattermost-redux/utils/post_utils';
import ChannelStore from 'stores/channel_store.jsx';
import NotificationStore from 'stores/notification_store.jsx';
import UserStore from 'stores/user_store.jsx';
import Constants, {NotificationLevels, UserStatuses} from 'utils/constants.jsx';
import {isSystemMessage} from 'utils/post_utils.jsx';
import {isMacApp, isMobileApp, isWindowsApp} from 'utils/user_agent.jsx';
import * as Utils from 'utils/utils.jsx';
import store from 'stores/redux_store.jsx';
......
......@@ -19,10 +19,10 @@ import ChannelStore from 'stores/channel_store.jsx';
import MessageWrapper from 'components/message_wrapper.jsx';
import {Constants, NotificationLevels, RHSStates, UserStatuses, ModalIdentifiers} from 'utils/constants.jsx';
import messageHtmlToComponent from 'utils/message_html_to_component';
import * as TextFormatting from 'utils/text_formatting.jsx';
import {getSiteURL} from 'utils/url.jsx';
import * as Utils from 'utils/utils.jsx';
import {messageHtmlToComponent} from 'utils/post_utils.jsx';
import ChannelInfoModal from 'components/channel_info_modal';
import ChannelInviteModal from 'components/channel_invite_modal';
import ChannelMembersModal from 'components/channel_members_modal';
......
......@@ -17,9 +17,9 @@ import TeamStore from 'stores/team_store.jsx';
import {browserHistory} from 'utils/browser_history';
import Constants from 'utils/constants.jsx';
import messageHtmlToComponent from 'utils/message_html_to_component';
import * as TextFormatting from 'utils/text_formatting.jsx';
import * as Utils from 'utils/utils.jsx';
import {messageHtmlToComponent} from 'utils/post_utils.jsx';
import logoImage from 'images/logo.png';
......
......@@ -4,10 +4,10 @@
import PropTypes from 'prop-types';
import React from 'react';
import messageHtmlToComponent from 'utils/message_html_to_component';
import * as TextFormatting from 'utils/text_formatting.jsx';
import {getSiteURL} from 'utils/url.jsx';
import * as Utils from 'utils/utils.jsx';
import {messageHtmlToComponent} from 'utils/post_utils.jsx';
export default class MessageWrapper extends React.Component {
constructor(props) {
......
......@@ -4,7 +4,7 @@
import PropTypes from 'prop-types';
import React from 'react';
import * as PostUtils from 'utils/post_utils.jsx';
import messageHtmlToComponent from 'utils/message_html_to_component';
import * as TextFormatting from 'utils/text_formatting.jsx';
import {renderSystemMessage} from './system_message_helpers.jsx';
......@@ -83,6 +83,6 @@ export default class PostMarkdown extends React.PureComponent {
}
const htmlFormattedText = TextFormatting.formatText(this.props.message, options);
return PostUtils.messageHtmlToComponent(htmlFormattedText, this.props.isRHS);
return messageHtmlToComponent(htmlFormattedText, this.props.isRHS);
}
}
......@@ -5,8 +5,8 @@ import PropTypes from 'prop-types';
import React from 'react';
import * as PostActions from 'actions/post_actions.jsx';
import messageHtmlToComponent from 'utils/message_html_to_component';
import * as TextFormatting from 'utils/text_formatting.jsx';
import {messageHtmlToComponent} from 'utils/post_utils.jsx';
import {isUrlSafe} from 'utils/url.jsx';
import {localizeMessage} from 'utils/utils.jsx';
......
......@@ -7,9 +7,9 @@ import {Client4} from 'mattermost-redux/client';
import store from 'stores/redux_store.jsx';
import {ActionTypes} from 'utils/constants.jsx';
import messageHtmlToComponent from 'utils/message_html_to_component';
import {getSiteURL} from 'utils/url.jsx';
import {formatText} from 'utils/text_formatting.jsx';
import {messageHtmlToComponent} from 'utils/post_utils.jsx';
window.plugins = {};
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PostUtils.containsAtChannel messageHtmlToComponent latex 1`] = `
exports[`messageHtmlToComponent latex 1`] = `
Array [
<p>
This is some latex!
......@@ -23,7 +23,7 @@ Array [
]
`;
exports[`PostUtils.containsAtChannel messageHtmlToComponent plain text 1`] = `
exports[`messageHtmlToComponent plain text 1`] = `
Array [
<p>
Hello, world!
......
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import messageHtmlToComponent from 'utils/message_html_to_component';
import * as TextFormatting from 'utils/text_formatting';
describe('messageHtmlToComponent', function() {
test('plain text', () => {
const input = 'Hello, world!';
const html = TextFormatting.formatText(input);
expect(messageHtmlToComponent(html)).toMatchSnapshot();
});
test('latex', () => {
const input = `This is some latex!
\`\`\`latex
x^2 + y^2 = z^2
\`\`\`
\`\`\`latex
F_m - 2 = F_0 F_1 \\dots F_{m-1}
\`\`\`
That was some latex!`;
const html = TextFormatting.formatText(input);
expect(messageHtmlToComponent(html)).toMatchSnapshot();
});
});
......@@ -4,7 +4,6 @@
import assert from 'assert';
import * as PostUtils from 'utils/post_utils.jsx';
import * as TextFormatting from 'utils/text_formatting.jsx';
describe('PostUtils.containsAtChannel', function() {
test('should return correct @all (same for @channel)', function() {
......@@ -171,29 +170,4 @@ describe('PostUtils.containsAtChannel', function() {
assert.equal(containsAtChannel, data.result, data.text);
}
});
describe('messageHtmlToComponent', () => {
test('plain text', () => {
const input = 'Hello, world!';
const html = TextFormatting.formatText(input);
expect(PostUtils.messageHtmlToComponent(html)).toMatchSnapshot();
});
test('latex', () => {
const input = `This is some latex!
\`\`\`latex
x^2 + y^2 = z^2
\`\`\`
\`\`\`latex
F_m - 2 = F_0 F_1 \\dots F_{m-1}
\`\`\`
That was some latex!`;
const html = TextFormatting.formatText(input);
expect(PostUtils.messageHtmlToComponent(html)).toMatchSnapshot();
});
});
});
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {Parser, ProcessNodeDefinitions} from 'html-to-react';
import AtMention from 'components/at_mention';
import LatexBlock from 'components/latex_block';
import MarkdownImage from 'components/markdown_image';
import PostEmoji from 'components/post_emoji';
/*
* Converts HTML to React components using html-to-react.
* The following options can be specified:
* - mentions - If specified, mentions are replaced with the AtMention component. Defaults to true.
* - emoji - If specified, emoji text is replaced with the PostEmoji component. Defaults to true.
* - images - If specified, markdown images are replaced with the PostMarkdown component. Defaults to true.
* - latex - If specified, latex is replaced with the LatexBlock component. Defaults to true.
*/
export function messageHtmlToComponent(html, isRHS, options = {}) {
if (!html) {
return null;
}
const parser = new Parser();
const processNodeDefinitions = new ProcessNodeDefinitions(React);
function isValidNode() {
return true;
}
const processingInstructions = [];
if (!('mentions' in options) || options.mentions) {
const mentionAttrib = 'data-mention';
processingInstructions.push({
replaceChildren: true,
shouldProcessNode: (node) => node.attribs && node.attribs[mentionAttrib],
processNode: (node, children) => {
const mentionName = node.attribs[mentionAttrib];
const callAtMention = (
<AtMention
mentionName={mentionName}
isRHS={isRHS}
hasMention={true}
>
{children}
</AtMention>
);
return callAtMention;
},
});
}
if (!('emoji' in options) || options.emoji) {
const emojiAttrib = 'data-emoticon';
processingInstructions.push({
replaceChildren: true,
shouldProcessNode: (node) => node.attribs && node.attribs[emojiAttrib],
processNode: (node) => {
const emojiName = node.attribs[emojiAttrib];
const callPostEmoji = (
<PostEmoji
name={emojiName}
/>
);
return callPostEmoji;
},
});
}
if (!('images' in options) || options.images) {
processingInstructions.push({
shouldProcessNode: (node) => node.type === 'tag' && node.name === 'img',
processNode: (node) => {
const {
class: className,
...attribs
} = node.attribs;
const callMarkdownImage = (
<MarkdownImage
className={className}
{...attribs}
/>
);
return callMarkdownImage;
},
});
}
if (!('latex' in options) || options.latex) {
processingInstructions.push({
shouldProcessNode: (node) => node.attribs && node.attribs['data-latex'],
processNode: (node) => {
return (
<LatexBlock content={node.attribs['data-latex']}/>
);
},
});
}
processingInstructions.push({
shouldProcessNode: () => true,
processNode: processNodeDefinitions.processDefaultNode,
});
return parser.parseWithInstructions(html, isValidNode, processingInstructions);
}
export default messageHtmlToComponent;
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {Parser, ProcessNodeDefinitions} from 'html-to-react';
import {Client4} from 'mattermost-redux/client';
import {getLicense, getConfig} from 'mattermost-redux/selectors/entities/general';
import {haveIChannelPermission} from 'mattermost-redux/selectors/entities/roles';
import {getChannel} from 'mattermost-redux/selectors/entities/channels';
import {Permissions} from 'mattermost-redux/constants';
import AtMention from 'components/at_mention';
import LatexBlock from 'components/latex_block';
import MarkdownImage from 'components/markdown_image';
import PostEmoji from 'components/post_emoji';
import UserStore from 'stores/user_store.jsx';
import store from 'stores/redux_store.jsx';
......@@ -157,100 +149,3 @@ export function containsAtChannel(text) {
return (/\B@(all|channel)\b/i).test(mentionableText);
}
/*
* Converts HTML to React components using html-to-react.
* The following options can be specified:
* - mentions - If specified, mentions are replaced with the AtMention component. Defaults to true.
* - emoji - If specified, emoji text is replaced with the PostEmoji component. Defaults to true.
* - images - If specified, markdown images are replaced with the PostMarkdown component. Defaults to true.
* - latex - If specified, latex is replaced with the LatexBlock component. Defaults to true.
*/
export function messageHtmlToComponent(html, isRHS, options = {}) {
if (!html) {
return null;
}
const parser = new Parser();
const processNodeDefinitions = new ProcessNodeDefinitions(React);
function isValidNode() {
return true;
}
const processingInstructions = [];
if (!('mentions' in options) || options.mentions) {
const mentionAttrib = 'data-mention';
processingInstructions.push({
replaceChildren: true,
shouldProcessNode: (node) => node.attribs && node.attribs[mentionAttrib],
processNode: (node, children) => {
const mentionName = node.attribs[mentionAttrib];
const callAtMention = (
<AtMention
mentionName={mentionName}
isRHS={isRHS}
hasMention={true}
>
{children}
</AtMention>
);
return callAtMention;
},
});
}
if (!('emoji' in options) || options.emoji) {
const emojiAttrib = 'data-emoticon';
processingInstructions.push({
replaceChildren: true,
shouldProcessNode: (node) => node.attribs && node.attribs[emojiAttrib],
processNode: (node) => {
const emojiName = node.attribs[emojiAttrib];
const callPostEmoji = (
<PostEmoji
name={emojiName}
/>
);
return callPostEmoji;
},
});
}
if (!('images' in options) || options.images) {
processingInstructions.push({
shouldProcessNode: (node) => node.type === 'tag' && node.name === 'img',
processNode: (node) => {
const {
class: className,
...attribs
} = node.attribs;
const callMarkdownImage = (
<MarkdownImage
className={className}
{...attribs}
/>
);
return callMarkdownImage;
},
});
}
if (!('latex' in options) || options.latex) {
processingInstructions.push({
shouldProcessNode: (node) => node.attribs && node.attribs['data-latex'],
processNode: (node) => {
return (
<LatexBlock content={node.attribs['data-latex']}/>
);
},
});
}
processingInstructions.push({
shouldProcessNode: () => true,
processNode: processNodeDefinitions.processDefaultNode,
});
return parser.parseWithInstructions(html, isValidNode, processingInstructions);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment