diff --git a/packages/shared/src/components/modals/post/CommentModal.spec.tsx b/packages/shared/src/components/modals/post/CommentModal.spec.tsx
new file mode 100644
index 00000000000..1c7b8991eea
--- /dev/null
+++ b/packages/shared/src/components/modals/post/CommentModal.spec.tsx
@@ -0,0 +1,181 @@
+import React, { type ReactNode } from 'react';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { render, screen } from '@testing-library/react';
+import CommentModal from './CommentModal';
+import Post from '../../../../__tests__/fixture/post';
+import loggedUser from '../../../../__tests__/fixture/loggedUser';
+import { useAuthContext } from '../../../contexts/AuthContext';
+import useCommentById from '../../../hooks/comments/useCommentById';
+import { useNotificationToggle } from '../../../hooks/notifications';
+import { useMutateComment } from '../../../hooks/post/useMutateComment';
+
+jest.mock('../common/Modal', () => {
+ const mockReact = jest.requireActual('react') as typeof React;
+
+ const MockModalBody = mockReact.forwardRef<
+ HTMLElement,
+ { children: ReactNode; className?: string }
+ >(({ children, className }, ref) => (
+
+ ));
+
+ MockModalBody.displayName = 'MockModalBody';
+
+ const Modal = ({ children }: { children: ReactNode }) => (
+
{children}
+ );
+
+ Modal.Body = MockModalBody;
+
+ return { Modal };
+});
+
+jest.mock('../../comments/CommentContainer', () => ({
+ __esModule: true,
+ default: () => ,
+}));
+
+jest.mock('../../fields/Switch', () => {
+ const mockReact = jest.requireActual('react') as typeof React;
+
+ const MockSwitch = mockReact.forwardRef<
+ HTMLLabelElement,
+ {
+ children: ReactNode;
+ className?: string;
+ labelClassName?: string;
+ }
+ >(({ children, className, labelClassName }, ref) => (
+
+ ));
+
+ MockSwitch.displayName = 'MockSwitch';
+
+ return {
+ Switch: MockSwitch,
+ };
+});
+
+jest.mock('../../fields/MarkdownInput/CommentMarkdownInput', () => {
+ const mockReact = jest.requireActual('react') as typeof React;
+
+ const MockCommentMarkdownInput = mockReact.forwardRef<
+ HTMLFormElement,
+ {
+ className?: Record;
+ formProps?: Record;
+ }
+ >(({ className, formProps }, ref) => (
+
+ ));
+
+ MockCommentMarkdownInput.displayName = 'MockCommentMarkdownInput';
+
+ return {
+ CommentMarkdownInput: MockCommentMarkdownInput,
+ };
+});
+
+jest.mock('../../../contexts/AuthContext', () => ({
+ useAuthContext: jest.fn(),
+}));
+
+jest.mock('../../../hooks/post/useMutateComment', () => ({
+ useMutateComment: jest.fn(),
+}));
+
+jest.mock('../../../hooks/notifications', () => ({
+ useNotificationToggle: jest.fn(),
+}));
+
+jest.mock('../../../hooks/comments/useCommentById', () => ({
+ __esModule: true,
+ default: jest.fn(),
+}));
+
+const mockUseAuthContext = useAuthContext as jest.Mock;
+const mockUseMutateComment = useMutateComment as jest.Mock;
+const mockUseNotificationToggle = useNotificationToggle as jest.Mock;
+const mockUseCommentById = useCommentById as jest.Mock;
+
+const defaultProps = {
+ isOpen: true,
+ onRequestClose: jest.fn(),
+ post: Post,
+};
+
+const renderComponent = () => {
+ const client = new QueryClient();
+
+ return render(
+
+
+ ,
+ );
+};
+
+describe('CommentModal', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ mockUseAuthContext.mockReturnValue({ user: loggedUser });
+ mockUseMutateComment.mockReturnValue({
+ mutateComment: jest.fn(),
+ isLoading: false,
+ isSuccess: false,
+ });
+ mockUseNotificationToggle.mockReturnValue({
+ shouldShowCta: true,
+ isEnabled: true,
+ onToggle: jest.fn(),
+ onSubmitted: jest.fn(),
+ });
+ mockUseCommentById.mockReturnValue({ comment: null });
+ });
+
+ it('should keep the mobile modal background on the scroll surface', () => {
+ renderComponent();
+
+ expect(screen.getByTestId('modal-body')).toHaveClass(
+ 'bg-background-default',
+ );
+ });
+
+ it('should keep the editor and notification switch in the normal flex flow', () => {
+ renderComponent();
+
+ const commentForm = screen.getByTestId('comment-form');
+
+ expect(commentForm).toHaveClass('flex', 'min-h-0');
+ expect(screen.getByTestId('markdown-container')).toHaveClass(
+ 'min-h-0',
+ 'flex-1',
+ );
+ expect(screen.getByTestId('notification-switch')).toBeInTheDocument();
+ expect(commentForm).not.toHaveAttribute('style');
+ });
+});
diff --git a/packages/shared/src/components/modals/post/CommentModal.tsx b/packages/shared/src/components/modals/post/CommentModal.tsx
index f25a984414a..c307be759de 100644
--- a/packages/shared/src/components/modals/post/CommentModal.tsx
+++ b/packages/shared/src/components/modals/post/CommentModal.tsx
@@ -1,11 +1,5 @@
import type { ReactElement } from 'react';
-import React, {
- useCallback,
- useLayoutEffect,
- useMemo,
- useRef,
- useState,
-} from 'react';
+import React, { useCallback, useLayoutEffect, useMemo, useState } from 'react';
import type { QueryClient } from '@tanstack/react-query';
import { useQueryClient } from '@tanstack/react-query';
import classNames from 'classnames';
@@ -15,7 +9,6 @@ import { FormWrapper } from '../../fields/form';
import type { CommentMarkdownInputProps } from '../../fields/MarkdownInput/CommentMarkdownInput';
import { CommentMarkdownInput } from '../../fields/MarkdownInput/CommentMarkdownInput';
import { useMutateComment } from '../../../hooks/post/useMutateComment';
-import { useVisualViewport } from '../../../hooks/utils/useVisualViewport';
import type { Comment, PostCommentsData } from '../../../graphql/comments';
import { useNotificationToggle } from '../../../hooks/notifications';
import { NotificationPromptSource } from '../../../lib/log';
@@ -79,7 +72,6 @@ const getCommentFromCache = ({
return undefined;
};
-
export interface CommentModalProps
extends LazyModalCommonProps,
CommentMarkdownInputProps {
@@ -97,11 +89,6 @@ export default function CommentModal({
post,
initialContent: initialContentFromProps,
}: CommentModalProps): ReactElement {
- const inputRef = useRef(null);
- const headerRef = useRef(null);
- const replyRef = useRef(null);
- const switchRef = useRef(null);
-
const { user } = useAuthContext();
const client = useQueryClient();
const [modalNode, setModalNode] = useState(null);
@@ -166,20 +153,6 @@ export default function CommentModal({
modalNode?.scrollTo?.({ behavior: 'auto', top: 10000 });
}, [modalNode]);
- const { height } = useVisualViewport();
- const replyHeight = replyRef.current?.clientHeight ?? 0;
- const footerHeight = switchRef.current?.clientHeight ?? 0;
- const headerHeight = headerRef.current?.offsetHeight ?? 0;
- const totalHeight = height - headerHeight - replyHeight - footerHeight;
- const inputHeight = totalHeight > 0 ? Math.max(totalHeight, 300) : 'auto';
-
- if (
- inputRef.current &&
- inputRef.current.style?.height !== `${inputHeight}px`
- ) {
- inputRef.current.style.height = `${inputHeight}px`;
- }
-
const { submitCopy, initialContent } = useMemo(() => {
if (isEdit) {
return {
@@ -217,7 +190,7 @@ export default function CommentModal({
className="!border-none"
overlayClassName="!touch-none"
>
-
+
@@ -235,10 +208,10 @@ export default function CommentModal({
disabled: isSuccess,
}}
className={{
- container: 'flex-1 first:!border-none',
+ container:
+ 'flex min-h-full flex-1 flex-col bg-background-default first:!border-none',
header: 'sticky top-0 z-2 w-full bg-background-default',
}}
- headerRef={headerRef}
>
{isReply && comment && (
<>
@@ -251,10 +224,7 @@ export default function CommentModal({
postAuthorId={post?.author?.id}
postScoutId={post?.scout?.id}
/>
-
+
Reply to
{comment.author?.username}
@@ -263,16 +233,15 @@ export default function CommentModal({
>
)}
Receive updates when other members engage