MM-52902 : Pasting text or image in center message input box shifts input box up unexpectedly (#23502)

This commit is contained in:
M-ZubairAhmed 2023-05-26 17:50:07 +05:30 committed by GitHub
parent 0a897220a7
commit 0e31ecbbe8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 92 additions and 115 deletions

View File

@ -311,9 +311,8 @@ describe('components/AdvancedCreateComment', () => {
expect(wrapper.state().serverError.message).toBe(testError1);
expect(wrapper.state().draft.uploadsInProgress).toEqual([2, 3]);
// clientId = -1
const testError2 = 'test error 2';
instance.handleUploadError(testError2, -1, null, props.rootId);
instance.handleUploadError(testError2, '', null, props.rootId);
// should not call onUpdateCommentDraft
expect(updateCommentDraftWithRootId.mock.calls.length).toBe(1);

View File

@ -1116,8 +1116,8 @@ class AdvancedCreateComment extends React.PureComponent<Props, State> {
}
};
handleUploadError = (err: string | ServerError | null, clientId: string | number = -1, _?: string, rootId = '') => {
if (clientId !== -1) {
handleUploadError = (uploadError: string | ServerError | null, clientId?: string, _?: string, rootId = '') => {
if (clientId) {
const draft = {...this.draftsForPost[rootId]!};
const uploadsInProgress = [...draft.uploadsInProgress];
@ -1137,16 +1137,13 @@ class AdvancedCreateComment extends React.PureComponent<Props, State> {
}
}
let serverError = err;
if (typeof serverError === 'string') {
serverError = new Error(serverError);
if (typeof uploadError === 'string') {
if (uploadError.length !== 0) {
this.setState({serverError: new Error(uploadError)});
}
this.setState({serverError}, () => {
if (serverError && this.props.scrollToBottom) {
this.props.scrollToBottom();
} else {
this.setState({serverError: uploadError});
}
});
};
removePreview = (id: string) => {

View File

@ -1038,17 +1038,8 @@ class AdvancedCreatePost extends React.PureComponent<Props, State> {
this.handleDraftChange(draft, true);
};
handleUploadError = (err: string | ServerError, clientId?: string, channelId?: string) => {
let serverError = err;
if (typeof serverError === 'string') {
serverError = new Error(serverError);
}
if (!channelId || !clientId) {
this.setState({serverError});
return;
}
handleUploadError = (uploadError: string | ServerError | null, clientId?: string, channelId?: string) => {
if (clientId && channelId) {
const draft = {...this.draftsForChannel[channelId]!};
if (draft.uploadsInProgress) {
@ -1064,8 +1055,15 @@ class AdvancedCreatePost extends React.PureComponent<Props, State> {
this.draftsForChannel[channelId] = modifiedDraft;
}
}
}
this.setState({serverError});
if (typeof uploadError === 'string') {
if (uploadError.length !== 0) {
this.setState({serverError: new Error(uploadError)});
}
} else {
this.setState({serverError: uploadError});
}
};
removePreview = (id: string) => {

View File

@ -88,7 +88,7 @@ type Props = {
hideEmojiPicker: () => void;
toggleAdvanceTextEditor: () => void;
handleUploadProgress: (filePreviewInfo: FilePreviewInfo) => void;
handleUploadError: (err: string | ServerError, clientId?: string, channelId?: string) => void;
handleUploadError: (err: string | ServerError | null, clientId?: string, channelId?: string) => void;
handleFileUploadComplete: (fileInfos: FileInfo[], clientIds: string[], channelId: string, rootId?: string) => void;
handleUploadStart: (clientIds: string[], channelId: string) => void;
handleFileUploadChange: () => void;
@ -199,17 +199,6 @@ const AdvanceTextEditor = ({
setKeepEditorInFocus(true);
}, []);
let serverErrorJsx = null;
if (serverError) {
serverErrorJsx = (
<MessageSubmitError
error={serverError}
submittedMessage={serverError.submittedMessage}
handleSubmit={handleSubmit}
/>
);
}
let attachmentPreview = null;
if (!readOnlyChannel && (draft.fileInfos.length > 0 || draft.uploadsInProgress.length > 0)) {
attachmentPreview = (
@ -551,12 +540,20 @@ const AdvanceTextEditor = ({
<div
id='postCreateFooter'
role='form'
className={classNames('AdvancedTextEditor__footer', {
'AdvancedTextEditor__footer--has-error': postError || serverError,
})}
className={classNames('AdvancedTextEditor__footer', {'AdvancedTextEditor__footer--has-error': postError || serverError})}
>
{postError && <label className={classNames('post-error', {errorClass})}>{postError}</label>}
{serverErrorJsx}
{postError && (
<label className={classNames('post-error', {errorClass})}>
{postError}
</label>
)}
{serverError && (
<MessageSubmitError
error={serverError}
submittedMessage={serverError.submittedMessage}
handleSubmit={handleSubmit}
/>
)}
<MsgTyping
channelId={channelId}
postId={postId}

View File

@ -3,17 +3,15 @@
import React, {MouseEvent, DragEvent, ChangeEvent} from 'react';
import {FileInfo} from '@mattermost/types/files';
import {General} from 'mattermost-redux/constants';
import {clearFileInput} from 'utils/utils';
import {shallowWithIntl} from 'tests/helpers/intl-test-helper';
import FileUpload, {FileUpload as FileUploadClass} from 'components/file_upload/file_upload';
import {clearFileInput} from 'utils/utils';
import {FilesWillUploadHook} from 'types/store/plugins';
import {FileInfo} from '@mattermost/types/files';
import {shallowWithIntl} from 'tests/helpers/intl-test-helper';
const generatedIdRegex = /[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}/;
@ -266,7 +264,7 @@ describe('components/FileUpload', () => {
);
expect(baseProps.onUploadError).toHaveBeenCalledTimes(1);
expect(baseProps.onUploadError).toHaveBeenCalledWith('');
expect(baseProps.onUploadError).toHaveBeenCalledWith(null);
});
test('should error max upload files', () => {
@ -286,7 +284,7 @@ describe('components/FileUpload', () => {
expect(baseProps.onUploadStart).toBeCalledWith([], props.channelId);
expect(baseProps.onUploadError).toHaveBeenCalledTimes(2);
expect(baseProps.onUploadError.mock.calls[0][0]).toEqual('');
expect(baseProps.onUploadError.mock.calls[0][0]).toEqual(null);
});
test('should error max upload files', () => {
@ -306,7 +304,7 @@ describe('components/FileUpload', () => {
expect(baseProps.onUploadStart).toBeCalledWith([], props.channelId);
expect(baseProps.onUploadError).toHaveBeenCalledTimes(2);
expect(baseProps.onUploadError.mock.calls[0][0]).toEqual('');
expect(baseProps.onUploadError.mock.calls[0][0]).toEqual(null);
});
test('should error max too large files', () => {
@ -324,7 +322,7 @@ describe('components/FileUpload', () => {
expect(baseProps.onUploadStart).toBeCalledWith([], baseProps.channelId);
expect(baseProps.onUploadError).toHaveBeenCalledTimes(2);
expect(baseProps.onUploadError.mock.calls[0][0]).toEqual('');
expect(baseProps.onUploadError.mock.calls[0][0]).toEqual(null);
});
test('should functions when handleChange is called', () => {
@ -358,7 +356,7 @@ describe('components/FileUpload', () => {
instance.handleDrop(e);
expect(baseProps.onUploadError).toBeCalled();
expect(baseProps.onUploadError).toHaveBeenCalledWith('');
expect(baseProps.onUploadError).toHaveBeenCalledWith(null);
expect(instance.uploadFiles).toBeCalled();
expect(instance.uploadFiles).toHaveBeenCalledWith(e.dataTransfer.files);
@ -386,7 +384,7 @@ describe('components/FileUpload', () => {
expect(baseProps.onUploadStart).toHaveBeenCalledTimes(0);
expect(baseProps.onUploadError).toHaveBeenCalledTimes(1);
expect(baseProps.onUploadError).toHaveBeenCalledWith('');
expect(baseProps.onUploadError).toHaveBeenCalledWith(null);
});
test('FilesWillUploadHook - should reject one file and allow one file', () => {
@ -409,6 +407,6 @@ describe('components/FileUpload', () => {
expect(baseProps.onUploadStart).toHaveBeenCalledWith([expect.stringMatching(generatedIdRegex)], props.channelId);
expect(baseProps.onUploadError).toHaveBeenCalledTimes(1);
expect(baseProps.onUploadError).toHaveBeenCalledWith('');
expect(baseProps.onUploadError).toHaveBeenCalledWith(null);
});
});

View File

@ -120,7 +120,7 @@ export type Props = {
/**
* Function to be called when upload fails
*/
onUploadError: (err: string | ServerError, clientId?: string, channelId?: string, currentRootId?: string) => void;
onUploadError: (err: string | ServerError | null, clientId?: string, channelId?: string, currentRootId?: string) => void;
/**
* Function to be called when file upload starts
@ -222,13 +222,13 @@ export class FileUpload extends PureComponent<Props, State> {
pluginUploadFiles = (files: File[]) => {
// clear any existing errors
this.props.onUploadError('');
this.props.onUploadError(null);
this.uploadFiles(files);
};
checkPluginHooksAndUploadFiles = (files: FileList | File[]) => {
// clear any existing errors
this.props.onUploadError('');
this.props.onUploadError(null);
let sortedFiles = Array.from(files).sort((a, b) => a.name.localeCompare(b.name, this.props.locale, {numeric: true}));
@ -335,7 +335,7 @@ export class FileUpload extends PureComponent<Props, State> {
return;
}
this.props.onUploadError('');
this.props.onUploadError(null);
const items = e.dataTransfer.items || [];
const droppedFiles = e.dataTransfer.files;
@ -460,7 +460,7 @@ export class FileUpload extends PureComponent<Props, State> {
return;
}
this.props.onUploadError('');
this.props.onUploadError(null);
const items = [];
for (let i = 0; i < e.clipboardData.items.length; i++) {

View File

@ -1,68 +1,56 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {ReactFragment} from 'react';
import React, {MouseEventHandler} from 'react';
import {FormattedMessage} from 'react-intl';
import {ServerError} from '@mattermost/types/errors';
import {isErrorInvalidSlashCommand} from 'utils/post_utils';
interface MessageSubmitErrorProps {
interface Props {
error: ServerError;
handleSubmit: (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void;
handleSubmit: MouseEventHandler<HTMLAnchorElement>;
submittedMessage?: string;
}
class MessageSubmitError extends React.PureComponent<MessageSubmitErrorProps> {
public renderSlashCommandError = (): string | ReactFragment => {
if (!this.props.submittedMessage) {
return this.props.error.message;
}
function MessageSubmitError(props: Props) {
if (isErrorInvalidSlashCommand(props.error)) {
const slashCommand = props.submittedMessage?.split(' ')[0];
const command = this.props.submittedMessage.split(' ')[0];
return (
<React.Fragment>
<div className='has-error'>
<label className='control-label'>
<FormattedMessage
id='message_submit_error.invalidCommand'
defaultMessage="Command with a trigger of ''{command}'' not found. "
defaultMessage="Command with a trigger of ''{slashCommand}'' not found. "
values={{
command,
slashCommand,
}}
/>
<a
href='#'
onClick={this.props.handleSubmit}
onClick={props.handleSubmit}
>
<FormattedMessage
id='message_submit_error.sendAsMessageLink'
defaultMessage='Click here to send as a message.'
/>
</a>
</React.Fragment>
);
};
public render(): JSX.Element | null {
const error = this.props.error;
if (!error) {
return null;
}
let errorContent: string | ReactFragment = error.message;
if (isErrorInvalidSlashCommand(error)) {
errorContent = this.renderSlashCommandError();
}
return (
<div className='has-error'>
<label className='control-label'>
{errorContent}
</label>
</div>
);
}
if (props.error?.message?.trim()?.length === 0) {
return null;
}
return (
<div className='has-error'>
<label className='control-label'>{props.error.message.trim()}</label>
</div>
);
}
export default MessageSubmitError;