[MM-55339] Improve types for Markdown component (#25350)

This commit is contained in:
M-ZubairAhmed 2023-11-09 14:17:22 +05:30 committed by GitHub
parent ee6457c3df
commit cd58a5baaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 134 additions and 180 deletions

View File

@ -231,7 +231,6 @@ exports[`components/drafts/panel/panel_body should have called handleFormattedTe
} }
channelNamesMap={Object {}} channelNamesMap={Object {}}
dispatch={[Function]} dispatch={[Function]}
editedAt={0}
emojiMap={ emojiMap={
EmojiMap { EmojiMap {
"customEmojis": Map {}, "customEmojis": Map {},
@ -240,7 +239,6 @@ exports[`components/drafts/panel/panel_body should have called handleFormattedTe
} }
enableFormatting={true} enableFormatting={true}
hasImageProxy={false} hasImageProxy={false}
imagesMetadata={Object {}}
managedResourcePaths={Array []} managedResourcePaths={Array []}
mentionKeys={Array []} mentionKeys={Array []}
message="message" message="message"
@ -251,8 +249,6 @@ exports[`components/drafts/panel/panel_body should have called handleFormattedTe
"mentionHighlight": false, "mentionHighlight": false,
} }
} }
postId=""
proxyImages={true}
siteURL="http://localhost:8065" siteURL="http://localhost:8065"
team={ team={
Object { Object {
@ -508,7 +504,6 @@ exports[`components/drafts/panel/panel_body should match snapshot 1`] = `
} }
channelNamesMap={Object {}} channelNamesMap={Object {}}
dispatch={[Function]} dispatch={[Function]}
editedAt={0}
emojiMap={ emojiMap={
EmojiMap { EmojiMap {
"customEmojis": Map {}, "customEmojis": Map {},
@ -517,7 +512,6 @@ exports[`components/drafts/panel/panel_body should match snapshot 1`] = `
} }
enableFormatting={true} enableFormatting={true}
hasImageProxy={false} hasImageProxy={false}
imagesMetadata={Object {}}
managedResourcePaths={Array []} managedResourcePaths={Array []}
mentionKeys={Array []} mentionKeys={Array []}
message="message" message="message"
@ -528,8 +522,6 @@ exports[`components/drafts/panel/panel_body should match snapshot 1`] = `
"mentionHighlight": false, "mentionHighlight": false,
} }
} }
postId=""
proxyImages={true}
siteURL="http://localhost:8065" siteURL="http://localhost:8065"
team={ team={
Object { Object {
@ -853,7 +845,6 @@ exports[`components/drafts/panel/panel_body should match snapshot for priority 1
} }
channelNamesMap={Object {}} channelNamesMap={Object {}}
dispatch={[Function]} dispatch={[Function]}
editedAt={0}
emojiMap={ emojiMap={
EmojiMap { EmojiMap {
"customEmojis": Map {}, "customEmojis": Map {},
@ -862,7 +853,6 @@ exports[`components/drafts/panel/panel_body should match snapshot for priority 1
} }
enableFormatting={true} enableFormatting={true}
hasImageProxy={false} hasImageProxy={false}
imagesMetadata={Object {}}
managedResourcePaths={Array []} managedResourcePaths={Array []}
mentionKeys={Array []} mentionKeys={Array []}
message="message" message="message"
@ -873,8 +863,6 @@ exports[`components/drafts/panel/panel_body should match snapshot for priority 1
"mentionHighlight": false, "mentionHighlight": false,
} }
} }
postId=""
proxyImages={true}
siteURL="http://localhost:8065" siteURL="http://localhost:8065"
team={ team={
Object { Object {
@ -1277,7 +1265,6 @@ exports[`components/drafts/panel/panel_body should match snapshot for requested_
} }
channelNamesMap={Object {}} channelNamesMap={Object {}}
dispatch={[Function]} dispatch={[Function]}
editedAt={0}
emojiMap={ emojiMap={
EmojiMap { EmojiMap {
"customEmojis": Map {}, "customEmojis": Map {},
@ -1286,7 +1273,6 @@ exports[`components/drafts/panel/panel_body should match snapshot for requested_
} }
enableFormatting={true} enableFormatting={true}
hasImageProxy={false} hasImageProxy={false}
imagesMetadata={Object {}}
managedResourcePaths={Array []} managedResourcePaths={Array []}
mentionKeys={Array []} mentionKeys={Array []}
message="message" message="message"
@ -1297,8 +1283,6 @@ exports[`components/drafts/panel/panel_body should match snapshot for requested_
"mentionHighlight": false, "mentionHighlight": false,
} }
} }
postId=""
proxyImages={true}
siteURL="http://localhost:8065" siteURL="http://localhost:8065"
team={ team={
Object { Object {

View File

@ -1,30 +1,31 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/Markdown should not render markdown when formatting is disabled 1`] = ` exports[`components/Markdown should not render markdown when formatting is disabled 1`] = `
<span> <div>
This _is_ some **Markdown** <p>
<Connect(PostEditedIndicator) This
editedAt={0} <em>
postId="" is
/> </em>
</span> some
<strong>
Markdown
</strong>
</p>
</div>
`; `;
exports[`components/Markdown should render properly 1`] = ` exports[`components/Markdown should render properly 1`] = `
<p <div>
key="0" <p>
> This
This <em>
<em is
key="1" </em>
> some
is <strong>
</em> Markdown
some </strong>
<strong </p>
key="3" </div>
>
Markdown
</strong>
</p>
`; `;

View File

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {connect} from 'react-redux'; import {connect, type ConnectedProps} from 'react-redux';
import {Preferences} from 'mattermost-redux/constants'; import {Preferences} from 'mattermost-redux/constants';
import {createSelector} from 'mattermost-redux/selectors/create_selector'; import {createSelector} from 'mattermost-redux/selectors/create_selector';
@ -14,24 +14,17 @@ import {getCurrentTeam} from 'mattermost-redux/selectors/entities/teams';
import {getEmojiMap} from 'selectors/emojis'; import {getEmojiMap} from 'selectors/emojis';
import type {ChannelNamesMap, MentionKey} from 'utils/text_formatting';
import {getSiteURL} from 'utils/url'; import {getSiteURL} from 'utils/url';
import type {GlobalState} from 'types/store'; import type {GlobalState} from 'types/store';
import Markdown from './markdown'; import Markdown, {type OwnProps} from './markdown';
type Props = {
channelNamesMap?: ChannelNamesMap;
mentionKeys?: MentionKey[];
postId?: string;
}
function makeGetChannelNamesMap() { function makeGetChannelNamesMap() {
return createSelector( return createSelector(
'makeGetChannelNamesMap', 'makeGetChannelNamesMap',
getChannelNameToDisplayNameMap, getChannelNameToDisplayNameMap,
(state: GlobalState, props: Props) => props && props.channelNamesMap, (state: GlobalState, props: OwnProps) => props && props.channelNamesMap,
(channelNamesMap, channelMentions) => { (channelNamesMap, channelMentions) => {
if (channelMentions) { if (channelMentions) {
return Object.assign({}, channelMentions, channelNamesMap); return Object.assign({}, channelMentions, channelNamesMap);
@ -45,7 +38,7 @@ function makeGetChannelNamesMap() {
function makeMapStateToProps() { function makeMapStateToProps() {
const getChannelNamesMap = makeGetChannelNamesMap(); const getChannelNamesMap = makeGetChannelNamesMap();
return function mapStateToProps(state: GlobalState, ownProps: Props) { return function mapStateToProps(state: GlobalState, ownProps: OwnProps) {
const config = getConfig(state); const config = getConfig(state);
let channelId; let channelId;
@ -69,4 +62,7 @@ function makeMapStateToProps() {
}; };
} }
export default connect(makeMapStateToProps)(Markdown); const connector = connect(makeMapStateToProps);
export type PropsFromRedux = ConnectedProps<typeof connector>;
export default connector(Markdown);

View File

@ -1,13 +1,13 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import type {TeamType} from '@mattermost/types/teams'; import type {TeamType} from '@mattermost/types/teams';
import Markdown from 'components/markdown/markdown'; import Markdown from 'components/markdown';
import {renderWithContext} from 'tests/react_testing_utils';
import EmojiMap from 'utils/emoji_map'; import EmojiMap from 'utils/emoji_map';
import {TestHelper} from 'utils/test_helper'; import {TestHelper} from 'utils/test_helper';
@ -42,10 +42,8 @@ describe('components/Markdown', () => {
}; };
test('should render properly', () => { test('should render properly', () => {
const wrapper = shallow( const {container} = renderWithContext(<Markdown {...baseProps}/>);
<Markdown {...baseProps}/>, expect(container).toMatchSnapshot();
);
expect(wrapper).toMatchSnapshot();
}); });
test('should not render markdown when formatting is disabled', () => { test('should not render markdown when formatting is disabled', () => {
@ -54,9 +52,7 @@ describe('components/Markdown', () => {
enableFormatting: false, enableFormatting: false,
}; };
const wrapper = shallow( const {container} = renderWithContext(<Markdown {...props}/>);
<Markdown {...props}/>, expect(container).toMatchSnapshot();
);
expect(wrapper).toMatchSnapshot();
}); });
}); });

View File

@ -4,93 +4,35 @@
import React from 'react'; import React from 'react';
import type {PostImage, PostType} from '@mattermost/types/posts'; import type {PostImage, PostType} from '@mattermost/types/posts';
import type {Team} from '@mattermost/types/teams';
import PostEditedIndicator from 'components/post_view/post_edited_indicator'; import PostEditedIndicator from 'components/post_view/post_edited_indicator';
import type EmojiMap from 'utils/emoji_map'; import type EmojiMap from 'utils/emoji_map';
import messageHtmlToComponent from 'utils/message_html_to_component'; import messageHtmlToComponent from 'utils/message_html_to_component';
import type {ChannelNamesMap, MentionKey, TextFormattingOptions} from 'utils/text_formatting';
import {formatText} from 'utils/text_formatting'; import {formatText} from 'utils/text_formatting';
import type {ChannelNamesMap, TextFormattingOptions, MentionKey} from 'utils/text_formatting';
type Props = { import type {PropsFromRedux} from './index';
/* export type Props = PropsFromRedux & OwnProps;
* An object mapping channel names to channels for the current team
*/
channelNamesMap?: ChannelNamesMap;
/* export type OwnProps = {
* An array of URL schemes that should be turned into links. Anything that looks
* like a link will be turned into a link if this is not provided.
*/
autolinkedUrlSchemes?: string[];
/* /**
* Whether or not to do Markdown rendering
*/
enableFormatting?: boolean;
/*
* An array of paths on the server that are managed by another server
*/
managedResourcePaths?: string[];
/*
* An array of words that can be used to mention a user
*/
mentionKeys?: MentionKey[];
/*
* The text to be rendered
*/
message: string;
/*
* Any additional text formatting options to be used * Any additional text formatting options to be used
*/ */
options: Partial<TextFormattingOptions>; options?: Partial<TextFormattingOptions>;
/*
* The root Site URL for the page
*/
siteURL?: string;
/*
* The current team
*/
team?: Team;
/**
* If an image proxy is enabled.
*/
hasImageProxy?: boolean;
/**
* Minimum number of characters in a hashtag.
*/
minimumHashtagLength?: number;
/** /**
* Whether or not to proxy image URLs * Whether or not to proxy image URLs
*/ */
proxyImages?: boolean; proxyImages?: boolean;
/**
* Any extra props that should be passed into the image component
*/
imageProps?: object;
/** /**
* prop for passed down to image component for dimensions * prop for passed down to image component for dimensions
*/ */
imagesMetadata?: Record<string, PostImage>; imagesMetadata?: Record<string, PostImage>;
/**
* Whether or not to place the LinkTooltip component inside links
*/
hasPluginTooltips?: boolean;
/** /**
* Post id prop passed down to markdown image * Post id prop passed down to markdown image
*/ */
@ -101,13 +43,34 @@ type Props = {
*/ */
editedAt?: number; editedAt?: number;
/*
* The text to be rendered
*/
message?: string;
channelNamesMap?: ChannelNamesMap;
/*
* An array of words that can be used to mention a user
*/
mentionKeys?: MentionKey[];
/**
* Any extra props that should be passed into the image component
*/
imageProps?: object;
/**
* Whether or not to place the LinkTooltip component inside links
*/
hasPluginTooltips?: boolean;
channelId?: string; channelId?: string;
/** /**
* Post id prop passed down to markdown image * Post id prop passed down to markdown image
*/ */
postType?: PostType; postType?: PostType;
emojiMap: EmojiMap; emojiMap?: EmojiMap;
/** /**
* Some components processed by messageHtmlToComponent e.g. AtSumOfMembersMention require to have a list of userIds * Some components processed by messageHtmlToComponent e.g. AtSumOfMembersMention require to have a list of userIds
@ -120,59 +83,73 @@ type Props = {
messageMetadata?: Record<string, string>; messageMetadata?: Record<string, string>;
} }
export default class Markdown extends React.PureComponent<Props> { function Markdown({
static defaultProps: Partial<Props> = { options = {},
options: {}, proxyImages = true,
proxyImages: true, imagesMetadata = {},
imagesMetadata: {}, postId = '', // Needed to avoid proptypes console errors for cases like channel header, which doesn't have a proper value
postId: '', // Needed to avoid proptypes console errors for cases like channel header, which doesn't have a proper value editedAt = 0,
editedAt: 0, message = '',
}; channelNamesMap,
mentionKeys,
render() { imageProps,
const {postId, editedAt, message, enableFormatting} = this.props; channelId,
if (message === '' || !enableFormatting) { hasPluginTooltips,
return ( postType,
<span> emojiMap,
{message} userIds,
<PostEditedIndicator messageMetadata,
postId={postId} enableFormatting,
editedAt={editedAt} autolinkedUrlSchemes,
/> siteURL,
</span> hasImageProxy,
); team,
} minimumHashtagLength,
managedResourcePaths,
const options = Object.assign({ }: Props) {
autolinkedUrlSchemes: this.props.autolinkedUrlSchemes, if (message === '' || !enableFormatting) {
siteURL: this.props.siteURL, return (
mentionKeys: this.props.mentionKeys, <span>
atMentions: true, {message}
channelNamesMap: this.props.channelNamesMap, <PostEditedIndicator
proxyImages: this.props.hasImageProxy && this.props.proxyImages, postId={postId}
team: this.props.team, editedAt={editedAt}
minimumHashtagLength: this.props.minimumHashtagLength, />
managedResourcePaths: this.props.managedResourcePaths, </span>
editedAt, );
postId,
}, this.props.options);
const htmlFormattedText = formatText(message, options, this.props.emojiMap);
return messageHtmlToComponent(htmlFormattedText, {
imageProps: this.props.imageProps,
imagesMetadata: this.props.imagesMetadata,
hasPluginTooltips: this.props.hasPluginTooltips,
postId: this.props.postId,
userIds: this.props.userIds,
messageMetadata: this.props.messageMetadata,
channelId: this.props.channelId,
postType: this.props.postType,
mentionHighlight: this.props.options.mentionHighlight,
disableGroupHighlight: this.props.options.disableGroupHighlight,
editedAt,
atSumOfMembersMentions: this.props.options.atSumOfMembersMentions,
atPlanMentions: this.props.options.atPlanMentions,
});
} }
const inputOptions = Object.assign({
autolinkedUrlSchemes,
siteURL,
mentionKeys,
atMentions: true,
channelNamesMap,
proxyImages: hasImageProxy && proxyImages,
team,
minimumHashtagLength,
managedResourcePaths,
editedAt,
postId,
}, options);
const htmlFormattedText = formatText(message, inputOptions, emojiMap);
return messageHtmlToComponent(htmlFormattedText, {
imageProps,
imagesMetadata,
hasPluginTooltips,
postId,
userIds,
messageMetadata,
channelId,
postType,
mentionHighlight: options?.mentionHighlight,
disableGroupHighlight: options?.disableGroupHighlight,
editedAt,
atSumOfMembersMentions: options?.atSumOfMembersMentions,
atPlanMentions: options?.atPlanMentions,
});
} }
export default Markdown;