mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
MM-52902 : Pasting text or image in center message input box shifts input box up unexpectedly (#23502)
This commit is contained in:
parent
0a897220a7
commit
0e31ecbbe8
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
this.setState({serverError}, () => {
|
||||
if (serverError && this.props.scrollToBottom) {
|
||||
this.props.scrollToBottom();
|
||||
if (typeof uploadError === 'string') {
|
||||
if (uploadError.length !== 0) {
|
||||
this.setState({serverError: new Error(uploadError)});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.setState({serverError: uploadError});
|
||||
}
|
||||
};
|
||||
|
||||
removePreview = (id: string) => {
|
||||
|
@ -1038,34 +1038,32 @@ 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);
|
||||
}
|
||||
handleUploadError = (uploadError: string | ServerError | null, clientId?: string, channelId?: string) => {
|
||||
if (clientId && channelId) {
|
||||
const draft = {...this.draftsForChannel[channelId]!};
|
||||
|
||||
if (!channelId || !clientId) {
|
||||
this.setState({serverError});
|
||||
return;
|
||||
}
|
||||
if (draft.uploadsInProgress) {
|
||||
const index = draft.uploadsInProgress.indexOf(clientId);
|
||||
|
||||
const draft = {...this.draftsForChannel[channelId]!};
|
||||
|
||||
if (draft.uploadsInProgress) {
|
||||
const index = draft.uploadsInProgress.indexOf(clientId);
|
||||
|
||||
if (index !== -1) {
|
||||
const uploadsInProgress = draft.uploadsInProgress.filter((item, itemIndex) => index !== itemIndex);
|
||||
const modifiedDraft = {
|
||||
...draft,
|
||||
uploadsInProgress,
|
||||
};
|
||||
this.props.actions.setDraft(StoragePrefixes.DRAFT + channelId, modifiedDraft, channelId);
|
||||
this.draftsForChannel[channelId] = modifiedDraft;
|
||||
if (index !== -1) {
|
||||
const uploadsInProgress = draft.uploadsInProgress.filter((item, itemIndex) => index !== itemIndex);
|
||||
const modifiedDraft = {
|
||||
...draft,
|
||||
uploadsInProgress,
|
||||
};
|
||||
this.props.actions.setDraft(StoragePrefixes.DRAFT + channelId, modifiedDraft, channelId);
|
||||
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) => {
|
||||
|
@ -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}
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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++) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
const command = this.props.submittedMessage.split(' ')[0];
|
||||
return (
|
||||
<React.Fragment>
|
||||
<FormattedMessage
|
||||
id='message_submit_error.invalidCommand'
|
||||
defaultMessage="Command with a trigger of ''{command}'' not found. "
|
||||
values={{
|
||||
command,
|
||||
}}
|
||||
/>
|
||||
<a
|
||||
href='#'
|
||||
onClick={this.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();
|
||||
}
|
||||
function MessageSubmitError(props: Props) {
|
||||
if (isErrorInvalidSlashCommand(props.error)) {
|
||||
const slashCommand = props.submittedMessage?.split(' ')[0];
|
||||
|
||||
return (
|
||||
<div className='has-error'>
|
||||
<label className='control-label'>
|
||||
{errorContent}
|
||||
<FormattedMessage
|
||||
id='message_submit_error.invalidCommand'
|
||||
defaultMessage="Command with a trigger of ''{slashCommand}'' not found. "
|
||||
values={{
|
||||
slashCommand,
|
||||
}}
|
||||
/>
|
||||
<a
|
||||
href='#'
|
||||
onClick={props.handleSubmit}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='message_submit_error.sendAsMessageLink'
|
||||
defaultMessage='Click here to send as a message.'
|
||||
/>
|
||||
</a>
|
||||
</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;
|
||||
|
Loading…
Reference in New Issue
Block a user