diff --git a/.talismanrc b/.talismanrc index 384bfc36..0962f0a9 100644 --- a/.talismanrc +++ b/.talismanrc @@ -1,11 +1,13 @@ fileignoreconfig: -- filename: .github/workflows/secrets-scan.yml - ignore_detectors: - - filecontent -- filename: README.md - checksum: 568289bbe7c088967493db246dbf29e465382648ac574c1b1236be57d5662a38 -- filename: CHANGELOG.md - checksum: 677807c38f5135fac7ca0377e92953fb09097150ddd3c4f68667fe0368f916ee -- filename: src/visualBuilder/components/__test__/fieldToolbar.test.tsx - checksum: 3badd6a142456b6a361569e6fc546349a38ac6b366bef7fd5255d1e93220444e -version: "1.0" \ No newline at end of file + - filename: .github/workflows/secrets-scan.yml + ignore_detectors: + - filecontent + - filename: README.md + checksum: 568289bbe7c088967493db246dbf29e465382648ac574c1b1236be57d5662a38 + - filename: CHANGELOG.md + checksum: 677807c38f5135fac7ca0377e92953fb09097150ddd3c4f68667fe0368f916ee + - filename: src/visualBuilder/components/__test__/fieldToolbar.test.tsx + checksum: 3badd6a142456b6a361569e6fc546349a38ac6b366bef7fd5255d1e93220444e + - filename: src/visualBuilder/components/Collab/ThreadPopup/__test__/CommentTextArea.test.tsx + checksum: d0ef271ee5381d9feab06bda6e7e89bd0a882fee87495627bd811c1f0a5459c7 +version: "1.0" diff --git a/src/visualBuilder/components/Collab/ThreadPopup/CommentTextArea.tsx b/src/visualBuilder/components/Collab/ThreadPopup/CommentTextArea.tsx index 203842dc..01275762 100644 --- a/src/visualBuilder/components/Collab/ThreadPopup/CommentTextArea.tsx +++ b/src/visualBuilder/components/Collab/ThreadPopup/CommentTextArea.tsx @@ -102,12 +102,22 @@ const MentionSuggestionsList: React.FC<{ tabIndex={-1} aria-selected={index === selectedIndex} > - {(user.display?.length || 0) > 20 ? ( - - {(user.display || "").substring(0, 18) + "..."} - + {user.display == user.email ? ( + user.display.length > 20 ? ( + + {(user.display || "").substring(0, 18) + "..."} + + ) : ( + user.display + ) ) : ( - user.display || "" + + {user.display.length > 20 + ? (user.display || "").substring(0, 18) + "..." + : user.display} + )} ))} diff --git a/src/visualBuilder/components/Collab/ThreadPopup/__test__/CommentTextArea.test.tsx b/src/visualBuilder/components/Collab/ThreadPopup/__test__/CommentTextArea.test.tsx new file mode 100644 index 00000000..74cead4c --- /dev/null +++ b/src/visualBuilder/components/Collab/ThreadPopup/__test__/CommentTextArea.test.tsx @@ -0,0 +1,367 @@ +/** @jsxImportSource preact */ +import { render, screen, fireEvent } from "@testing-library/preact"; +import CommentTextArea from "../CommentTextArea"; +import { useCommentTextArea } from "../../../../hooks/useCommentTextArea"; +import { Mock } from "vitest"; +import React from "preact/compat"; +import classNames from "classnames"; + +// Mock dependencies +vi.mock("../../../../hooks/useCommentTextArea"); +vi.mock("../Tooltip/Tooltip", () => ({ + default: ({ children, content }) => ( +
{children}
+ ), +})); +vi.mock("../../../../collab.style", () => ({ + collabStyles: () => ({ + "collab-thread-input-indicator--error": "mocked-error-class", + "collab-thread-input-indicator--count": "mocked-count-class", + "collab-thread-body--input--wrapper": "mocked-wrapper-class", + "collab-thread-body--input": "mocked-input-class", + "collab-thread-body--input--textarea--wrapper": + "mocked-textarea-wrapper-class", + "collab-thread-body--input--textarea": "mocked-textarea-class", + "collab-thread-body--input--textarea--suggestionsList": + "mocked-suggestions-list-class", + "collab-thread-body--input--textarea--suggestionsList--item": + "mocked-suggestion-item-class", + "collab-thread-body--input--textarea--suggestionsList--item-selected": + "mocked-selected-item-class", + "collab-thread-input-indicator--wrapper": + "mocked-indicator-wrapper-class", + }), + flexAlignCenter: "flex-align-center-class", +})); + +const mockUserState = { + userMap: { + user1: { + uid: "user1", + email: "john.doe@example.com", + }, + }, + currentUser: { + uid: "user1", + email: "john.doe@example.com", + }, + mentionsList: [ + { + uid: "user1", + email: "john.doe@example.com", + display: "John Doe", + }, + { + uid: "user2", + email: "jane.smith@example.com", + display: "Jane Smith", + }, + ], +}; + +const mockComment = { + _id: "comment1", + threadUid: "thread-1", + message: "This is a comment", + author: "john.doe@example.com", + toUsers: [], + images: [], + createdAt: "2022-01-01T12:00:00Z", + createdBy: "user1", +}; + +const mockHandleOnSaveRef = { current: vi.fn() }; +const mockOnClose = vi.fn(); + +// Mock implementation of useCommentTextArea hook +const setupMockHook = (overrides = {}) => { + const defaultMock = { + state: { message: "Test message" }, + error: { message: "" }, + showSuggestions: false, + cursorPosition: { top: 20, showAbove: false }, + selectedIndex: 0, + filteredUsers: [], + inputRef: { current: null }, + listRef: { current: null }, + itemRefs: { current: [] }, + handleInputChange: vi.fn(), + handleKeyDown: vi.fn(), + handleSubmit: vi.fn(), + insertMention: vi.fn(), + maxMessageLength: 1000, + }; + + const mockImplementation = { ...defaultMock, ...overrides }; + (useCommentTextArea as Mock).mockReturnValue(mockImplementation); + return mockImplementation; +}; + +describe("CommentTextArea", () => { + beforeEach(() => { + setupMockHook(); + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + it("should render the textarea with correct placeholder", () => { + render( + + ); + + // Use a more reliable query that doesn't depend on exact placeholder text + const textarea = screen.getByRole("textbox"); + expect(textarea).toBeInTheDocument(); + + // Check if placeholder contains the expected text without worrying about exact quotes + const placeholder = textarea.getAttribute("placeholder"); + expect(placeholder).toContain("Enter a comment or tag others using"); + expect(placeholder).toContain("@"); + }); + + it("should set handleOnSaveRef.current to handleSubmit from the hook", () => { + const mockHook = setupMockHook(); + render( + + ); + + expect(mockHandleOnSaveRef.current).toBe(mockHook.handleSubmit); + }); + + it("should call handleInputChange when typing in textarea", () => { + const mockHook = setupMockHook(); + render( + + ); + + const textarea = screen.getByRole("textbox"); + fireEvent.change(textarea, { target: { value: "New text" } }); + + expect(mockHook.handleInputChange).toHaveBeenCalled(); + }); + + it("should call handleKeyDown when pressing keys in textarea", () => { + const mockHook = setupMockHook(); + render( + + ); + + const textarea = screen.getByRole("textbox"); + fireEvent.keyDown(textarea, { key: "Enter" }); + + expect(mockHook.handleKeyDown).toHaveBeenCalled(); + }); + + it("should display error message when there is an error", () => { + setupMockHook({ error: { message: "Error message" } }); + render( + + ); + + expect(screen.getByText("Error message")).toBeInTheDocument(); + }); + + it("should display character counter with correct values", () => { + setupMockHook({ state: { message: "Hello" }, maxMessageLength: 100 }); + render( + + ); + + expect(screen.getByText("5/100")).toBeInTheDocument(); + }); + + it("should render MentionSuggestionsList when showSuggestions is true", () => { + setupMockHook({ + showSuggestions: true, + filteredUsers: [ + { + uid: "user1", + email: "john@example.com", + display: "John Doe", + }, + { + uid: "user2", + email: "jane@example.com", + display: "Jane Smith", + }, + ], + selectedIndex: 0, + }); + + render( + + ); + + expect(screen.getByText("John Doe")).toBeInTheDocument(); + expect(screen.getByText("Jane Smith")).toBeInTheDocument(); + }); + + it("should not render MentionSuggestionsList when showSuggestions is false", () => { + setupMockHook({ + showSuggestions: false, + filteredUsers: [ + { + uid: "user1", + email: "john@example.com", + display: "John Doe", + }, + { + uid: "user2", + email: "jane@example.com", + display: "Jane Smith", + }, + ], + }); + + render( + + ); + + expect(screen.queryByText("John Doe")).not.toBeInTheDocument(); + expect(screen.queryByText("Jane Smith")).not.toBeInTheDocument(); + }); +}); + +// Test sub-components individually +describe("ErrorIndicator", () => { + it("should render error message", () => { + // We can access the ErrorIndicator through the CommentTextArea component + setupMockHook({ error: { message: "Test error message" } }); + render( + + ); + + expect(screen.getByText("Test error message")).toBeInTheDocument(); + }); +}); + +describe("CharacterCounter", () => { + it("should display current and max length", () => { + setupMockHook({ state: { message: "Test" }, maxMessageLength: 50 }); + render( + + ); + + expect(screen.getByText("4/50")).toBeInTheDocument(); + }); +}); + +describe("MentionSuggestionsList", () => { + it("should display filtered users and highlight the selected one", () => { + setupMockHook({ + showSuggestions: true, + filteredUsers: [ + { + uid: "user1", + email: "john@example.com", + display: "John Doe", + }, + { + uid: "user2", + email: "jane@example.com", + display: "Jane Smith", + }, + ], + selectedIndex: 1, + }); + + render( + + ); + + const selectedItem = screen.getByText("Jane Smith").closest("li"); + expect(selectedItem).toHaveAttribute("aria-selected", "true"); + }); + + it("should truncate long display names", () => { + setupMockHook({ + showSuggestions: true, + filteredUsers: [ + { + uid: "user3", + email: "very.long.email@example.com", + display: "Very Long Name That Should Be Truncated", + }, + ], + selectedIndex: 0, + }); + + render( + + ); + + // Use a more flexible approach to find truncated text + const truncatedTextElement = screen.getByText((content, element) => { + return ( + content.includes("Very Long Name") && + content.includes("...") && + element.tagName.toLowerCase() !== "html" && + element.tagName.toLowerCase() !== "body" + ); + }); + + expect(truncatedTextElement).toBeInTheDocument(); + }); +}); diff --git a/src/visualBuilder/eventManager/useCollab.ts b/src/visualBuilder/eventManager/useCollab.ts index 825ecb69..7f05cc2b 100644 --- a/src/visualBuilder/eventManager/useCollab.ts +++ b/src/visualBuilder/eventManager/useCollab.ts @@ -43,6 +43,10 @@ export const useCollab = () => { "collab.pauseFeedback", data?.data?.collab?.pauseFeedback ); + Config.set( + "collab.isFeedbackMode", + data?.data?.collab?.isFeedbackMode + ); showAllCollabIcons(); return; } diff --git a/src/visualBuilder/generators/generateThread.tsx b/src/visualBuilder/generators/generateThread.tsx index 850cac7e..8f54a506 100644 --- a/src/visualBuilder/generators/generateThread.tsx +++ b/src/visualBuilder/generators/generateThread.tsx @@ -376,6 +376,7 @@ export function hideAllCollabIcons(): void { ".visual-builder__collab-wrapper .collab-thread" ); icons?.forEach((icon) => icon?.classList.add(hiddenClass)); + toggleCollabPopup({ threadUid: "", action: "close" }); } export function showAllCollabIcons(): void {