[MM-54819] Convert ./components/external_image/external_image.tsx from Class Component to Function Component (#24941)

Этот коммит содержится в:
Gibson Han
2023-10-30 09:43:06 -07:00
коммит произвёл GitHub
родитель 7480fcfdf7
Коммит af8c9ae0ca
8 изменённых файлов: 140 добавлений и 144 удалений

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

@@ -23,7 +23,7 @@ describe('ExternalImage', () => {
};
test('should render an image', () => {
const wrapper = shallow<ExternalImage>(<ExternalImage {...baseProps}/>);
const wrapper = shallow(<ExternalImage {...baseProps}/>);
expect(baseProps.children).toHaveBeenCalledWith(baseProps.src);
expect(wrapper.find('img').exists()).toBe(true);
@@ -35,7 +35,7 @@ describe('ExternalImage', () => {
imageMetadata: undefined,
};
const wrapper = shallow<ExternalImage>(<ExternalImage {...props}/>);
const wrapper = shallow(<ExternalImage {...props}/>);
expect(baseProps.children).toHaveBeenCalledWith(baseProps.src);
expect(wrapper.find('img').exists()).toBe(true);
@@ -53,7 +53,7 @@ describe('ExternalImage', () => {
src: 'https://example.com/logo.svg',
};
const wrapper = shallow<ExternalImage>(<ExternalImage {...props}/>);
const wrapper = shallow(<ExternalImage {...props}/>);
expect(props.children).toHaveBeenCalledWith(props.src);
expect(wrapper.find('img').exists()).toBe(true);
@@ -72,7 +72,7 @@ describe('ExternalImage', () => {
src: 'https://example.com/logo.svg',
};
const wrapper = shallow<ExternalImage>(<ExternalImage {...props}/>);
const wrapper = shallow(<ExternalImage {...props}/>);
expect(props.children).toHaveBeenCalledWith('');
expect(wrapper.find('img').exists()).toBe(true);
@@ -84,85 +84,9 @@ describe('ExternalImage', () => {
hasImageProxy: true,
};
const wrapper = shallow<ExternalImage>(<ExternalImage {...props}/>);
const wrapper = shallow(<ExternalImage {...props}/>);
expect(props.children).toHaveBeenCalledWith(Client4.getBaseRoute() + '/image?url=' + encodeURIComponent(props.src));
expect(wrapper.find('img').exists()).toBe(true);
});
describe('isSVGImage', () => {
for (const testCase of [
{
name: 'no metadata, no extension',
src: 'https://example.com/image.png',
imageMetadata: undefined,
expected: false,
},
{
name: 'no metadata, svg extension',
src: 'https://example.com/image.svg',
imageMetadata: undefined,
expected: true,
},
{
name: 'no metadata, svg extension with query parameter',
src: 'https://example.com/image.svg?a=1',
imageMetadata: undefined,
expected: true,
},
{
name: 'no metadata, svg extension with hash',
src: 'https://example.com/image.svg#abc',
imageMetadata: undefined,
expected: true,
},
{
name: 'no metadata, proxied image',
src: 'https://mattermost.example.com/api/v4/image?url=' + encodeURIComponent('https://example.com/image.png'),
imageMetadata: undefined,
expected: false,
},
{
name: 'no metadata, proxied svg image',
src: 'https://mattermost.example.com/api/v4/image?url=' + encodeURIComponent('https://example.com/image.svg'),
imageMetadata: undefined,
expected: true,
},
{
name: 'with metadata, not an SVG',
src: 'https://example.com/image.png',
imageMetadata: {
format: 'png',
frameCount: 40,
width: 100,
height: 200,
},
expected: false,
},
{
name: 'with metadata, SVG',
src: 'https://example.com/image.svg',
imageMetadata: {
format: 'svg',
frameCount: 30,
width: 10,
height: 20,
},
expected: true,
},
]) {
test(testCase.name, () => {
const props = {
...baseProps,
src: testCase.src,
imageMetadata: testCase.imageMetadata,
};
const wrapper = shallow<ExternalImage>(<ExternalImage {...props}/>);
expect(wrapper.instance().isSVGImage()).toBe(testCase.expected);
});
}
});
});

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

@@ -1,13 +1,15 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import React, {memo} from 'react';
import type {PostImage} from '@mattermost/types/posts';
import {getImageSrc} from 'utils/post_utils';
interface Props {
import {isSVGImage} from './is_svg_image';
type Props = {
children: (src: string) => React.ReactNode;
enableSVGs: boolean;
hasImageProxy: boolean;
@@ -15,29 +17,13 @@ interface Props {
src: string;
}
export default class ExternalImage extends React.PureComponent<Props> {
isSVGImage = () => {
if (!this.props.imageMetadata) {
// Just check if the string contains an svg extension instead of if it ends with one because it avoids
// having to deal with query strings and proxied image URLs
return this.props.src.indexOf('.svg') !== -1;
}
return this.props.imageMetadata.format === 'svg';
};
shouldRenderImage = () => {
// Return true unless the image is an SVG and we have SVG rendering disabled
return this.props.enableSVGs || !this.isSVGImage();
};
render() {
let src = getImageSrc(this.props.src, this.props.hasImageProxy);
if (!this.shouldRenderImage()) {
src = '';
}
return this.props.children(src);
const ExternalImage = (props: Props) => {
const shouldRenderImage = props.enableSVGs || !isSVGImage(props.imageMetadata, props.src);
let src = getImageSrc(props.src, props.hasImageProxy);
if (!shouldRenderImage) {
src = '';
}
}
return (<>{props.children(src)}</>);
};
export default memo(ExternalImage);

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

@@ -0,0 +1,73 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {isSVGImage} from './is_svg_image';
describe('ExternalIImage isSVGImage', () => {
for (const testCase of [
{
name: 'no metadata, no extension',
src: 'https://example.com/image.png',
imageMetadata: undefined,
expected: false,
},
{
name: 'no metadata, svg extension',
src: 'https://example.com/image.svg',
imageMetadata: undefined,
expected: true,
},
{
name: 'no metadata, svg extension with query parameter',
src: 'https://example.com/image.svg?a=1',
imageMetadata: undefined,
expected: true,
},
{
name: 'no metadata, svg extension with hash',
src: 'https://example.com/image.svg#abc',
imageMetadata: undefined,
expected: true,
},
{
name: 'no metadata, proxied image',
src: 'https://mattermost.example.com/api/v4/image?url=' + encodeURIComponent('https://example.com/image.png'),
imageMetadata: undefined,
expected: false,
},
{
name: 'no metadata, proxied svg image',
src: 'https://mattermost.example.com/api/v4/image?url=' + encodeURIComponent('https://example.com/image.svg'),
imageMetadata: undefined,
expected: true,
},
{
name: 'with metadata, not an SVG',
src: 'https://example.com/image.png',
imageMetadata: {
format: 'png',
frameCount: 40,
width: 100,
height: 200,
},
expected: false,
},
{
name: 'with metadata, SVG',
src: 'https://example.com/image.svg',
imageMetadata: {
format: 'svg',
frameCount: 30,
width: 10,
height: 20,
},
expected: true,
},
]) {
test(testCase.name, () => {
const {imageMetadata, src} = testCase;
expect(isSVGImage(imageMetadata, src)).toBe(testCase.expected);
});
}
});

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

@@ -0,0 +1,13 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {PostImage} from '@mattermost/types/posts';
export const isSVGImage = (imageMetadata: PostImage | undefined, src: string) => {
if (!imageMetadata) {
// Just check if the string contains an svg extension instead of if it ends with one because it avoids
// having to deal with query strings and proxied image URLs
return src.indexOf('.svg') !== -1;
}
return imageMetadata.format === 'svg';
};

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

@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/MarkdownImage should match snapshot 1`] = `
<Connect(ExternalImage)
<Connect(Component)
imageMetadata={
Object {
"format": "png",
@@ -13,7 +13,7 @@ exports[`components/MarkdownImage should match snapshot 1`] = `
src="/images/logo.png"
>
<Component />
</Connect(ExternalImage)>
</Connect(Component)>
`;
exports[`components/MarkdownImage should match snapshot for SizeAwareImage dimensions 1`] = `
@@ -40,7 +40,7 @@ exports[`components/MarkdownImage should match snapshot for SizeAwareImage dimen
`;
exports[`components/MarkdownImage should match snapshot for broken link 1`] = `
<Connect(ExternalImage)
<Connect(Component)
imageMetadata={
Object {
"format": "png",
@@ -52,7 +52,7 @@ exports[`components/MarkdownImage should match snapshot for broken link 1`] = `
src="brokenLink"
>
<Component />
</Connect(ExternalImage)>
</Connect(Component)>
`;
exports[`components/MarkdownImage should provide image src as an alt text for MarkdownImageExpand if image has no own alt text 1`] = `

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

@@ -29,12 +29,12 @@ exports[`components/post_view/MessageAttachment should call actions.doPostAction
key="attachment__author-name"
location="message_attachment"
>
<Connect(ExternalImage)
<Connect(Component)
key="attachment__author-icon"
src="author_icon"
>
<Component />
</Connect(ExternalImage)>
</Connect(Component)>
<span
className="attachment__author-name"
key="attachment__author-name"
@@ -77,7 +77,7 @@ exports[`components/post_view/MessageAttachment should call actions.doPostAction
<div
className="attachment__image-container"
>
<Connect(ExternalImage)
<Connect(Component)
imageMetadata={
Object {
"height": 200,
@@ -87,16 +87,16 @@ exports[`components/post_view/MessageAttachment should call actions.doPostAction
src="image_url"
>
<Component />
</Connect(ExternalImage)>
</Connect(Component)>
</div>
<div
className="attachment__footer-container"
>
<Connect(ExternalImage)
<Connect(Component)
src="footer_icon"
>
<Component />
</Connect(ExternalImage)>
</Connect(Component)>
<span>
footer
</span>
@@ -121,7 +121,7 @@ exports[`components/post_view/MessageAttachment should call actions.doPostAction
<div
className="attachment__thumb-container"
>
<Connect(ExternalImage)
<Connect(Component)
imageMetadata={
Object {
"height": 200,
@@ -131,7 +131,7 @@ exports[`components/post_view/MessageAttachment should call actions.doPostAction
src="thumb_url"
>
<Component />
</Connect(ExternalImage)>
</Connect(Component)>
</div>
<div
style={
@@ -175,12 +175,12 @@ exports[`components/post_view/MessageAttachment should match snapshot 1`] = `
key="attachment__author-name"
location="message_attachment"
>
<Connect(ExternalImage)
<Connect(Component)
key="attachment__author-icon"
src="author_icon"
>
<Component />
</Connect(ExternalImage)>
</Connect(Component)>
<span
className="attachment__author-name"
key="attachment__author-name"
@@ -223,7 +223,7 @@ exports[`components/post_view/MessageAttachment should match snapshot 1`] = `
<div
className="attachment__image-container"
>
<Connect(ExternalImage)
<Connect(Component)
imageMetadata={
Object {
"height": 200,
@@ -233,16 +233,16 @@ exports[`components/post_view/MessageAttachment should match snapshot 1`] = `
src="image_url"
>
<Component />
</Connect(ExternalImage)>
</Connect(Component)>
</div>
<div
className="attachment__footer-container"
>
<Connect(ExternalImage)
<Connect(Component)
src="footer_icon"
>
<Component />
</Connect(ExternalImage)>
</Connect(Component)>
<span>
footer
</span>
@@ -251,7 +251,7 @@ exports[`components/post_view/MessageAttachment should match snapshot 1`] = `
<div
className="attachment__thumb-container"
>
<Connect(ExternalImage)
<Connect(Component)
imageMetadata={
Object {
"height": 200,
@@ -261,7 +261,7 @@ exports[`components/post_view/MessageAttachment should match snapshot 1`] = `
src="thumb_url"
>
<Component />
</Connect(ExternalImage)>
</Connect(Component)>
</div>
<div
style={
@@ -317,12 +317,12 @@ exports[`components/post_view/MessageAttachment should match snapshot when no fo
key="attachment__author-name"
location="message_attachment"
>
<Connect(ExternalImage)
<Connect(Component)
key="attachment__author-icon"
src="author_icon"
>
<Component />
</Connect(ExternalImage)>
</Connect(Component)>
<span
className="attachment__author-name"
key="attachment__author-name"
@@ -365,7 +365,7 @@ exports[`components/post_view/MessageAttachment should match snapshot when no fo
<div
className="attachment__image-container"
>
<Connect(ExternalImage)
<Connect(Component)
imageMetadata={
Object {
"height": 200,
@@ -375,13 +375,13 @@ exports[`components/post_view/MessageAttachment should match snapshot when no fo
src="image_url"
>
<Component />
</Connect(ExternalImage)>
</Connect(Component)>
</div>
</div>
<div
className="attachment__thumb-container"
>
<Connect(ExternalImage)
<Connect(Component)
imageMetadata={
Object {
"height": 200,
@@ -391,7 +391,7 @@ exports[`components/post_view/MessageAttachment should match snapshot when no fo
src="thumb_url"
>
<Component />
</Connect(ExternalImage)>
</Connect(Component)>
</div>
<div
style={

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

@@ -182,7 +182,7 @@ exports[`PostAttachmentOpenGraphImage should match snapshot 1`] = `
<AutoHeightSwitcher
showSlot={1}
slot1={
<Memo(Connect(ExternalImage))
<Memo(Connect(Component))
imageMetadata={
Object {
"format": "png",
@@ -197,7 +197,7 @@ exports[`PostAttachmentOpenGraphImage should match snapshot 1`] = `
src="http://localhost:8065/api/v4/image?url=http%3A%2F%2Fmattermo…t.com%2Fwp-content%2Fuploads%2F2021%2F09%2FHomepage%402x.png"
>
[Function]
</Memo(Connect(ExternalImage))>
</Memo(Connect(Component))>
}
slot2={
<button
@@ -240,7 +240,7 @@ exports[`PostAttachmentOpenGraphImage should match snapshot 1`] = `
}
>
<div>
<Connect(ExternalImage)
<Connect(Component)
imageMetadata={
Object {
"format": "png",
@@ -254,7 +254,7 @@ exports[`PostAttachmentOpenGraphImage should match snapshot 1`] = `
}
src="http://localhost:8065/api/v4/image?url=http%3A%2F%2Fmattermo…t.com%2Fwp-content%2Fuploads%2F2021%2F09%2FHomepage%402x.png"
>
<ExternalImage
<Memo(ExternalImage)
dispatch={[Function]}
enableSVGs={true}
hasImageProxy={true}
@@ -299,8 +299,8 @@ exports[`PostAttachmentOpenGraphImage should match snapshot 1`] = `
src="/api/v4/image?url=http%3A%2F%2Flocalhost%3A8065%2Fapi%2Fv4%2Fimage%3Furl%3Dhttp%253A%252F%252Fmattermo%E2%80%A6t.com%252Fwp-content%252Fuploads%252F2021%252F09%252FHomepage%25402x.png"
/>
</figure>
</ExternalImage>
</Connect(ExternalImage)>
</Memo(ExternalImage)>
</Connect(Component)>
</div>
</div>
</Transition>

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

@@ -70,10 +70,10 @@ exports[`YoutubeVideo should match init snapshot 1`] = `
<div
className="video-thumbnail__container"
>
<Connect(ExternalImage)
<Connect(Component)
src="linkForThumbnail"
>
<ExternalImage
<Memo(ExternalImage)
dispatch={[Function]}
enableSVGs={false}
hasImageProxy={false}
@@ -84,8 +84,8 @@ exports[`YoutubeVideo should match init snapshot 1`] = `
className="video-thumbnail"
src="linkForThumbnail"
/>
</ExternalImage>
</Connect(ExternalImage)>
</Memo(ExternalImage)>
</Connect(Component)>
<div
className="block"
>