MM-23523 Migrate "components/markdown_image/*" to typescript (#23524)

Этот коммит содержится в:
Sai Deepesh
2023-07-21 20:30:19 +05:30
коммит произвёл GitHub
родитель ab38a8fbd7
Коммит 16606f4420
4 изменённых файлов: 115 добавлений и 58 удалений

Просмотреть файл

@@ -5,7 +5,7 @@ exports[`components/MarkdownImage should match snapshot 1`] = `
imageMetadata={
Object {
"format": "png",
"frame_count": 0,
"frameCount": 0,
"height": 90,
"width": 1041,
}
@@ -23,22 +23,32 @@ exports[`components/MarkdownImage should match snapshot for SizeAwareImage dimen
dimensions={
Object {
"format": "jpg",
"frame_count": 0,
"frameCount": 0,
"height": 90,
"width": 100,
}
}
height=""
onClick={[Function]}
onImageLoadFail={[Function]}
onImageLoaded={[Function]}
showLoader={false}
src="safeSrc"
title="test title"
width=""
/>
`;
exports[`components/MarkdownImage should match snapshot for broken link 1`] = `
<Connect(ExternalImage)
imageMetadata={Object {}}
imageMetadata={
Object {
"format": "png",
"frameCount": 0,
"height": 10,
"width": 10,
}
}
src="brokenLink"
>
<Component />
@@ -53,7 +63,7 @@ exports[`components/MarkdownImage should provide image src as an alt text for Ma
postId="post_id"
>
<SizeAwareImage
alt={null}
alt=""
className="markdown-inline-img markdown-inline-img--hover cursor--pointer a11y--active"
dimensions={Object {}}
height="250"
@@ -93,16 +103,19 @@ exports[`components/MarkdownImage should render an image with no preview if the
dimensions={
Object {
"format": "png",
"frame_count": 0,
"frameCount": 0,
"height": 90,
"width": 1041,
}
}
height=""
onClick={[Function]}
onImageLoadFail={[Function]}
onImageLoaded={[Function]}
showLoader={false}
src="https://example.com/image.png"
title="test title"
width=""
/>
</div>
`;
@@ -115,16 +128,19 @@ exports[`components/MarkdownImage should render an image with preview modal if t
dimensions={
Object {
"format": "png",
"frame_count": 0,
"frameCount": 0,
"height": 90,
"width": 1041,
}
}
height=""
onClick={[Function]}
onImageLoadFail={[Function]}
onImageLoaded={[Function]}
showLoader={false}
src="https://example.com/image.png"
title="test title"
width=""
/>
</div>
`;

Просмотреть файл

@@ -2,15 +2,16 @@
// See LICENSE.txt for license information.
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {ActionCreatorsMapObject, bindActionCreators, Dispatch} from 'redux';
import {Action, GenericAction} from 'mattermost-redux/types/actions';
import {openModal} from 'actions/views/modals';
import MarkdownImage from './markdown_image';
import MarkdownImage, {Props} from './markdown_image';
function mapDispatchToProps(dispatch) {
function mapDispatchToProps(dispatch: Dispatch<GenericAction>) {
return {
actions: bindActionCreators({
actions: bindActionCreators<ActionCreatorsMapObject<Action>, Props['actions']>({
openModal,
}, dispatch),
};

Просмотреть файл

@@ -18,13 +18,18 @@ describe('components/MarkdownImage', () => {
format: 'png',
height: 90,
width: 1041,
frame_count: 0,
frameCount: 0,
},
alt: 'test image',
height: '',
width: '',
title: 'test title',
className: 'markdown-inline-img',
postId: 'post_id',
imageIsLink: false,
onImageLoaded: jest.fn(),
onImageHeightChanged: jest.fn(),
postType: 'system_generic',
actions: {
openModal: jest.fn(),
},
@@ -40,7 +45,14 @@ describe('components/MarkdownImage', () => {
});
test('should match snapshot for broken link', () => {
const props = {...baseProps, imageMetadata: {}, src: 'brokenLink'};
const props = {...baseProps,
imageMetadata: {
format: 'png',
height: 10,
width: 10,
frameCount: 0,
},
src: 'brokenLink'};
const wrapper = shallow(
<MarkdownImage {...props}/>,
);
@@ -49,20 +61,36 @@ describe('components/MarkdownImage', () => {
});
test('should handle load failure properly', () => {
const props = {...baseProps, imageMetadata: {}, src: 'brokenLink'};
const props = {...baseProps,
imageMetadata: {
format: 'png',
height: 10,
width: 10,
frameCount: 0,
},
src: 'brokenLink'};
const wrapper = shallow(
<MarkdownImage {...props}/>,
);
expect(wrapper.state('loadFailed')).toBe(false);
wrapper.instance().handleLoadFail();
(wrapper.instance() as MarkdownImage).handleLoadFail();
expect(wrapper.state('loadFailed')).toBe(true);
});
test('should reset loadFailed state after image source is updated', () => {
const props = {...baseProps, imageMetadata: {}, src: 'brokenLink'};
const props = {...baseProps,
imageMetadata: {
format: 'png',
height: 10,
width: 10,
frameCount: 0,
},
src: 'brokenLink'};
const nextProps = {...baseProps, src: 'https://example.com/image.png'};
const wrapper = shallow(
<MarkdownImage {...props}/>,
@@ -133,7 +161,7 @@ describe('components/MarkdownImage', () => {
expect(wrapper.state('loaded')).toBe(false);
wrapper.instance().handleImageLoaded(dimensions);
(wrapper.instance() as MarkdownImage).handleImageLoaded(dimensions);
expect(wrapper.state('loaded')).toBe(true);
@@ -143,7 +171,7 @@ describe('components/MarkdownImage', () => {
it('should match snapshot for SizeAwareImage dimensions', () => {
const props = {...baseProps,
imageMetadata: {format: 'jpg', frame_count: 0, width: 100, height: 90},
imageMetadata: {format: 'jpg', frameCount: 0, width: 100, height: 90},
src: 'path/image',
};
const wrapper = shallow(
@@ -198,8 +226,10 @@ describe('components/MarkdownImage', () => {
const wrapper = shallow(
<MarkdownImage {...props}/>,
);
wrapper.instance().showModal({preventDefault: () => {}}, 'https://example.com/image.png');
const mockEvent = {
preventDefault: () => {},
};
(wrapper.instance() as MarkdownImage).showModal(mockEvent as unknown as React.MouseEvent<HTMLImageElement>, 'https://example.com/image.png');
expect(props.actions.openModal).toHaveBeenCalledTimes(1);
});
@@ -281,7 +311,7 @@ describe('components/MarkdownImage', () => {
test('should provide image src as an alt text for MarkdownImageExpand if image has no own alt text', () => {
const props = {
alt: null,
alt: '',
title: 'test title',
className: 'markdown-inline-img',
postId: 'post_id',

Просмотреть файл

@@ -1,48 +1,53 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import PropTypes from 'prop-types';
import React from 'react';
import React, {PureComponent, MouseEvent} from 'react';
import Constants, {ModalIdentifiers} from 'utils/constants';
import {Post, PostImage} from '@mattermost/types/posts';
import {ModalData} from 'types/actions';
import MarkdownImageExpand from 'components/markdown_image_expand';
import ExternalImage from 'components/external_image';
import SizeAwareImage from 'components/size_aware_image';
import FilePreviewModal from 'components/file_preview_modal';
import brokenImageIcon from 'images/icons/brokenimage.png';
import ExternalLink from 'components/external_link';
export default class MarkdownImage extends React.PureComponent {
static defaultProps = {
imageMetadata: {},
import brokenImageIcon from 'images/icons/brokenimage.png';
import Constants, {ModalIdentifiers} from 'utils/constants';
export type Props = {
alt: string;
imageMetadata?: PostImage;
src: string;
// height and width come from the Markdown renderer as either "auto" or a string containing a number.
height: string;
width: string;
title: string;
className: string;
postId: Post['id'];
imageIsLink: boolean;
onImageLoaded?: ({height, width}: {height: number; width: number}) => void;
onImageHeightChanged?: (isExpanded: boolean) => void;
postType?: string;
actions: {
openModal: <P>(modalData: ModalData<P>) => void;
};
hideUtilities?: boolean;
};
type State = {
loadFailed: boolean;
loaded: boolean;
};
export default class MarkdownImage extends PureComponent<Props, State> {
static defaultProps: Partial<Props> = {
imageMetadata: {} as PostImage,
};
static propTypes = {
alt: PropTypes.string,
imageMetadata: PropTypes.object,
src: PropTypes.string.isRequired,
// height and width come from the Markdown renderer as either "auto" or a string containing a number.
height: PropTypes.string,
width: PropTypes.string,
title: PropTypes.string,
className: PropTypes.string.isRequired,
postId: PropTypes.string.isRequired,
imageIsLink: PropTypes.bool.isRequired,
onImageLoaded: PropTypes.func,
onImageHeightChanged: PropTypes.func,
postType: PropTypes.string,
actions: PropTypes.shape({
openModal: PropTypes.func,
}).isRequired,
hideUtilities: PropTypes.bool,
};
constructor(props) {
constructor(props: Props) {
super(props);
this.state = {
@@ -58,6 +63,10 @@ export default class MarkdownImage extends React.PureComponent {
width,
} = this.props;
if (!imageMetadata) {
return 0;
}
if (!height) {
return imageMetadata.height;
}
@@ -71,12 +80,12 @@ export default class MarkdownImage extends React.PureComponent {
return parseInt(height, 10);
};
getFileExtensionFromUrl = (url) => {
getFileExtensionFromUrl = (url: string) => {
const index = url.lastIndexOf('.');
return index > 0 ? url.substring(index + 1) : null;
};
showModal = (e, link) => {
showModal = (e: MouseEvent<HTMLImageElement>, link: string) => {
const extension = this.getFileExtensionFromUrl(link);
if (!this.props.imageIsLink && extension) {
@@ -86,11 +95,12 @@ export default class MarkdownImage extends React.PureComponent {
modalId: ModalIdentifiers.FILE_PREVIEW_MODAL,
dialogType: FilePreviewModal,
dialogProps: {
startIndex: 0,
postId: this.props.postId,
fileInfos: [{
has_preview_image: false,
link,
extension: this.props.imageMetadata.format || extension,
extension: this.props?.imageMetadata?.format ?? extension,
name: this.props.alt,
}],
},
@@ -107,17 +117,17 @@ export default class MarkdownImage extends React.PureComponent {
this.props.postType === Constants.PostTypes.HEADER_CHANGE;
};
componentDidUpdate(prevProps) {
componentDidUpdate(prevProps: Props) {
this.onUpdated(prevProps.src);
}
onUpdated = (prevSrc) => {
onUpdated = (prevSrc: string) => {
if (this.props.src && this.props.src !== prevSrc) {
this.setState({loadFailed: false});
}
};
handleImageLoaded = ({height, width}) => {
handleImageLoaded = ({height, width}: {height: number; width: number}) => {
this.setState({
loaded: true,
}, () => { // Call onImageLoaded prop only after state has already been set