From 972cc829584cc6203012738e09f842da5a492c01 Mon Sep 17 00:00:00 2001 From: Sanne de Vries <65487235+sanne-san@users.noreply.github.com> Date: Mon, 9 Dec 2024 14:42:24 +0100 Subject: [PATCH] Prevented top-level comment input from closing when it has text (#21795) REF https://linear.app/ghost/issue/PLG-298/ - When you're typing a comment, and exit the input field, it collapses into a non-editable state; you first have to click on it again to "open" the form. This means you can't select the text or instantly start typing again. When the input has a value, we should stop it from closing. - added custom `useEditor` hook that wraps TipTap and exposes both the `editor` and `hasContent` props keeping logic out of the consuming components --------- Co-authored-by: Kevin Ansfield --- .../src/components/content/forms/EditForm.tsx | 13 ++--- .../src/components/content/forms/Form.tsx | 31 +++++------- .../src/components/content/forms/MainForm.tsx | 27 +++++------ .../components/content/forms/ReplyForm.tsx | 13 ++--- apps/comments-ui/src/utils/editor.ts | 7 ++- apps/comments-ui/src/utils/hooks.ts | 38 ++++++++++++++- apps/comments-ui/test/e2e/content.test.ts | 7 +-- apps/comments-ui/test/e2e/editor.test.ts | 1 + apps/comments-ui/test/e2e/main-form.test.ts | 48 +++++++++++++++++++ 9 files changed, 130 insertions(+), 55 deletions(-) create mode 100644 apps/comments-ui/test/e2e/main-form.test.ts diff --git a/apps/comments-ui/src/components/content/forms/EditForm.tsx b/apps/comments-ui/src/components/content/forms/EditForm.tsx index 970a3f7207..11e36c2345 100644 --- a/apps/comments-ui/src/components/content/forms/EditForm.tsx +++ b/apps/comments-ui/src/components/content/forms/EditForm.tsx @@ -1,9 +1,8 @@ import {Comment, OpenCommentForm, useAppContext} from '../../../AppContext'; import {Form} from './Form'; -import {getEditorConfig} from '../../../utils/editor'; import {isMobile} from '../../../utils/helpers'; -import {useCallback, useEffect} from 'react'; -import {useEditor} from '@tiptap/react'; +import {useCallback, useEffect, useMemo} from 'react'; +import {useEditor} from '../../../utils/hooks'; type Props = { openForm: OpenCommentForm; @@ -14,17 +13,15 @@ type Props = { const EditForm: React.FC = ({comment, openForm, parent}) => { const {dispatchAction, t} = useAppContext(); - const config = { + const editorConfig = useMemo(() => ({ placeholder: t('Edit this comment'), // warning: we cannot use autofocus on the edit field, because that sets // the cursor position at the beginning of the text field instead of the end autofocus: false, content: comment.html - }; + }), [comment]); - const editor = useEditor({ - ...getEditorConfig(config) - }); + const {editor} = useEditor(editorConfig); // Instead of autofocusing, we focus and jump to end manually useEffect(() => { diff --git a/apps/comments-ui/src/components/content/forms/Form.tsx b/apps/comments-ui/src/components/content/forms/Form.tsx index 3f81a20eef..15c3c32abd 100644 --- a/apps/comments-ui/src/components/content/forms/Form.tsx +++ b/apps/comments-ui/src/components/content/forms/Form.tsx @@ -22,30 +22,26 @@ export type FormEditorProps = { submitText: React.ReactNode; submitSize: SubmitSize; openForm?: OpenCommentForm; - initialHasContent?: boolean; }; -export const FormEditor: React.FC = ({comment, submit, progress, setProgress, close, isOpen, editor, submitText, submitSize, openForm, initialHasContent}) => { +export const FormEditor: React.FC = ({comment, submit, progress, setProgress, close, isOpen, editor, submitText, submitSize, openForm}) => { const labs = useLabs(); const {dispatchAction, t} = useAppContext(); let buttonIcon = null; - const [hasContent, setHasContent] = useState(initialHasContent || false); useEffect(() => { - if (editor) { + if (editor && openForm) { const checkContent = () => { - const editorHasContent = !editor.isEmpty; - setHasContent(editorHasContent); + const hasUnsavedChanges = comment && openForm.type === 'edit' ? + editor.getHTML() !== comment.html : + !editor.isEmpty; - if (openForm) { - const hasUnsavedChanges = comment && openForm.type === 'edit' ? editor.getHTML() !== comment.html : editorHasContent; - - // avoid unnecessary state updates to prevent infinite loops - if (openForm.hasUnsavedChanges !== hasUnsavedChanges) { - dispatchAction('setCommentFormHasUnsavedChanges', {id: openForm.id, hasUnsavedChanges}); - } + // avoid unnecessary state updates to prevent infinite loops + if (openForm.hasUnsavedChanges !== hasUnsavedChanges) { + dispatchAction('setCommentFormHasUnsavedChanges', {id: openForm.id, hasUnsavedChanges}); } }; + editor.on('update', checkContent); editor.on('transaction', checkContent); @@ -152,7 +148,7 @@ export const FormEditor: React.FC = ({comment, submit, progress