docbrown/app/javascript/article-form/components/EditorBody.jsx
Suzanne Aitchison 75d6e60a95
✂️ Remove reach combobox ✂️ (#17728)
* WIP: new autocomplete, search executed, dropdown positioned

* WIP: most features working in Editor

* handle textarea blur

* fix bug with id not attached to textarea

* updates to textAreaUtils tests

* aria live, fix border style

* replace comment text area

* fix some height and style issues

* update preact tests

* clean up styles, rename files, use portal for popover

* clean up styles, rename files, use portal for popover

* scroll popover items into view if necessary

* refactor cypress specs

* actually remove the package

* add some more specs

* remove storybook stories for now

* remove relative class causing issues

* small fixes for cypress specs

* make live region assertive to ensure change to combobox is known

* some comments

* add a storybook story

* replace % font size, add explainer to story
2022-07-11 12:50:52 +01:00

108 lines
3.3 KiB
JavaScript

import { h } from 'preact';
import PropTypes from 'prop-types';
import { useLayoutEffect, useRef } from 'preact/hooks';
import { Toolbar } from './Toolbar';
import { handleImagePasted } from './pasteImageHelpers';
import {
handleImageDrop,
handleImageFailure,
onDragOver,
onDragExit,
} from './dragAndDropHelpers';
import { usePasteImage } from '@utilities/pasteImage';
import { useDragAndDrop } from '@utilities/dragAndDrop';
import { fetchSearch } from '@utilities/search';
import { AutocompleteTriggerTextArea } from '@crayons/AutocompleteTriggerTextArea';
function handleImageSuccess(textAreaRef) {
return function (response) {
// Function is within the component to be able to access
// textarea ref.
const editableBodyElement = textAreaRef.current;
const { links } = response;
const markdownImageLink = `![Image description](${links[0]})\n`;
const { selectionStart, selectionEnd, value } = editableBodyElement;
const before = value.substring(0, selectionStart);
const after = value.substring(selectionEnd, value.length);
editableBodyElement.value = `${before + markdownImageLink} ${after}`;
editableBodyElement.selectionStart =
selectionStart + markdownImageLink.length;
editableBodyElement.selectionEnd = editableBodyElement.selectionStart;
// Dispatching a new event so that linkstate, https://github.com/developit/linkstate,
// the function used to create the onChange prop gets called correctly.
editableBodyElement.dispatchEvent(new Event('input'));
};
}
export const EditorBody = ({
onChange,
defaultValue,
switchHelpContext,
version,
}) => {
const textAreaRef = useRef(null);
const { setElement } = useDragAndDrop({
onDrop: handleImageDrop(
handleImageSuccess(textAreaRef),
handleImageFailure,
),
onDragOver,
onDragExit,
});
const setPasteElement = usePasteImage({
onPaste: handleImagePasted(
handleImageSuccess(textAreaRef),
handleImageFailure,
),
});
useLayoutEffect(() => {
if (textAreaRef.current) {
setElement(textAreaRef.current);
setPasteElement(textAreaRef.current);
}
});
return (
<div
data-testid="article-form__body"
className="crayons-article-form__body drop-area text-padding h-100"
>
<Toolbar version={version} textAreaId="article_body_markdown" />
<AutocompleteTriggerTextArea
triggerCharacter="@"
maxSuggestions={6}
searchInstructionsMessage="Type to search for a user"
ref={textAreaRef}
fetchSuggestions={(username) =>
fetchSearch('usernames', { username }).then(({ result }) =>
result.map((user) => ({ ...user, value: user.username })),
)
}
autoResize
onChange={onChange}
onFocus={switchHelpContext}
aria-label="Post Content"
name="body_markdown"
id="article_body_markdown"
defaultValue={defaultValue}
placeholder="Write your post content here..."
className="crayons-textfield crayons-textfield--ghost crayons-article-form__body__field ff-monospace fs-l h-100"
/>
</div>
);
};
EditorBody.propTypes = {
onChange: PropTypes.func.isRequired,
defaultValue: PropTypes.string.isRequired,
switchHelpContext: PropTypes.func.isRequired,
version: PropTypes.string.isRequired,
};
EditorBody.displayName = 'EditorBody';