maintain cursor position while inserting an image (#15508)
* maintain cursor position while inserting an image * undo small unintended change
This commit is contained in:
parent
7caafc8ea7
commit
c9b097e574
3 changed files with 62 additions and 13 deletions
|
|
@ -182,6 +182,7 @@ const V2EditorImageUpload = ({
|
|||
{...buttonProps}
|
||||
icon={ImageIcon}
|
||||
onClick={(e) => {
|
||||
buttonProps.onClick?.(e);
|
||||
useNativeUpload
|
||||
? initNativeImagePicker(e)
|
||||
: document.getElementById('image-upload-field').click();
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ const getPreviousMatchingSibling = (element, selector) => {
|
|||
export const MarkdownToolbar = ({ textAreaId }) => {
|
||||
const [textArea, setTextArea] = useState(null);
|
||||
const [overflowMenuOpen, setOverflowMenuOpen] = useState(false);
|
||||
const [storedCursorPosition, setStoredCursorPosition] = useState({});
|
||||
const smallScreen = useMediaQuery(`(max-width: ${BREAKPOINTS.Medium - 1}px)`);
|
||||
|
||||
const markdownSyntaxFormatters = {
|
||||
|
|
@ -190,9 +191,11 @@ export const MarkdownToolbar = ({ textAreaId }) => {
|
|||
};
|
||||
|
||||
const handleImageUploadStarted = () => {
|
||||
const { textBeforeSelection, textAfterSelection, selectionEnd } =
|
||||
const { textBeforeSelection, textAfterSelection } =
|
||||
getSelectionData(textArea);
|
||||
|
||||
const { selectionEnd } = storedCursorPosition;
|
||||
|
||||
const textWithPlaceholder = `${textBeforeSelection}\n${UPLOADING_IMAGE_PLACEHOLDER}${textAfterSelection}`;
|
||||
textArea.value = textWithPlaceholder;
|
||||
// Make sure Editor text area updates via linkstate
|
||||
|
|
@ -203,27 +206,46 @@ export const MarkdownToolbar = ({ textAreaId }) => {
|
|||
// Set cursor to the end of the placeholder
|
||||
const newCursorPosition =
|
||||
selectionEnd + UPLOADING_IMAGE_PLACEHOLDER.length + 1;
|
||||
|
||||
textArea.setSelectionRange(newCursorPosition, newCursorPosition);
|
||||
};
|
||||
|
||||
const handleImageUploadSuccess = (imageMarkdown) => {
|
||||
const handleImageUploadEnd = (imageMarkdown = '') => {
|
||||
const {
|
||||
selectionStart,
|
||||
selectionEnd,
|
||||
value: currentTextAreaValue,
|
||||
} = textArea;
|
||||
|
||||
const indexOfPlaceholder = currentTextAreaValue.indexOf(
|
||||
UPLOADING_IMAGE_PLACEHOLDER,
|
||||
);
|
||||
|
||||
// User has deleted placeholder, nothing to do
|
||||
if (indexOfPlaceholder === -1) return;
|
||||
|
||||
const newTextValue = textArea.value.replace(
|
||||
UPLOADING_IMAGE_PLACEHOLDER,
|
||||
imageMarkdown,
|
||||
);
|
||||
textArea.value = newTextValue;
|
||||
// Make sure Editor text area updates via linkstate
|
||||
textArea.dispatchEvent(new Event('input'));
|
||||
};
|
||||
|
||||
const handleImageUploadError = () => {
|
||||
const newTextValue = textArea.value.replace(
|
||||
UPLOADING_IMAGE_PLACEHOLDER,
|
||||
'',
|
||||
);
|
||||
textArea.value = newTextValue;
|
||||
// Make sure Editor text area updates via linkstate
|
||||
textArea.dispatchEvent(new Event('input'));
|
||||
|
||||
// The change to image markdown length does not affect cursor position
|
||||
if (indexOfPlaceholder > selectionStart) {
|
||||
textArea.setSelectionRange(selectionStart, selectionEnd);
|
||||
return;
|
||||
}
|
||||
|
||||
const differenceInLength =
|
||||
imageMarkdown.length - UPLOADING_IMAGE_PLACEHOLDER.length;
|
||||
|
||||
textArea.setSelectionRange(
|
||||
selectionStart + differenceInLength,
|
||||
selectionEnd + differenceInLength,
|
||||
);
|
||||
};
|
||||
|
||||
const getSecondaryFormatterButtons = (isOverflow) =>
|
||||
|
|
@ -308,10 +330,14 @@ export const MarkdownToolbar = ({ textAreaId }) => {
|
|||
<ImageUploader
|
||||
editorVersion="v2"
|
||||
onImageUploadStart={handleImageUploadStarted}
|
||||
onImageUploadSuccess={handleImageUploadSuccess}
|
||||
onImageUploadError={handleImageUploadError}
|
||||
onImageUploadSuccess={handleImageUploadEnd}
|
||||
onImageUploadError={handleImageUploadEnd}
|
||||
buttonProps={{
|
||||
onKeyUp: (e) => handleToolbarButtonKeyPress(e, 'toolbar-btn'),
|
||||
onClick: () => {
|
||||
const { selectionStart, selectionEnd } = textArea;
|
||||
setStoredCursorPosition({ selectionStart, selectionEnd });
|
||||
},
|
||||
tooltip: smallScreen ? null : (
|
||||
<span aria-hidden="true">Upload image</span>
|
||||
),
|
||||
|
|
|
|||
|
|
@ -98,6 +98,28 @@ describe('Upload image', () => {
|
|||
.should('have.text', 'Error message');
|
||||
});
|
||||
|
||||
it('maintains writing position when image is uploaded', () => {
|
||||
cy.intercept('/image_uploads', { delay: 2000 });
|
||||
|
||||
cy.findByLabelText('Post Content').as('editorBody');
|
||||
// Type some text and place the cursor between one & two
|
||||
cy.get('@editorBody')
|
||||
.clear()
|
||||
.type('one two{leftarrow}{leftarrow}{leftarrow}');
|
||||
|
||||
cy.findByLabelText(/Upload image/, { selector: 'button' }).click();
|
||||
cy.findByLabelText(/Upload image/, { selector: 'input' }).attachFile(
|
||||
'/images/admin-image.png',
|
||||
);
|
||||
|
||||
// Check that when we continue typing after selecting an image, my cursor is after the placeholder text, which was inserted at my cursor position
|
||||
cy.get('@editorBody').should('have.focus').type(' more text ');
|
||||
cy.get('@editorBody').should(
|
||||
'have.value',
|
||||
'one \n more text two',
|
||||
);
|
||||
});
|
||||
|
||||
it('Uploads a cover image in the editor', () => {
|
||||
cy.findByRole('form', { name: 'Edit post' }).within(() => {
|
||||
cy.findByLabelText(/Add a cover image/).attachFile(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue