diff --git a/.mcp.json b/.mcp.json
index ee5d245dcb..28fa0cb286 100644
--- a/.mcp.json
+++ b/.mcp.json
@@ -1,7 +1,7 @@
{
"mcpServers": {
"next-devtools": {
- "command": "npx",
+ "command": "/opt/homebrew/bin/npx",
"args": ["-y", "next-devtools-mcp@latest"]
}
}
diff --git a/__tests__/components/utils/select/dropdown/CommonDropdownItemsMobileWrapper.test.tsx b/__tests__/components/utils/select/dropdown/CommonDropdownItemsMobileWrapper.test.tsx
index 159d11e60e..2b63aafd03 100644
--- a/__tests__/components/utils/select/dropdown/CommonDropdownItemsMobileWrapper.test.tsx
+++ b/__tests__/components/utils/select/dropdown/CommonDropdownItemsMobileWrapper.test.tsx
@@ -1,25 +1,78 @@
-import { render, screen, fireEvent } from '@testing-library/react';
-import React from 'react';
-import CommonDropdownItemsMobileWrapper from '@/components/utils/select/dropdown/CommonDropdownItemsMobileWrapper';
+import { fireEvent, render, screen } from "@testing-library/react";
+import React from "react";
+import CommonDropdownItemsMobileWrapper from "@/components/utils/select/dropdown/CommonDropdownItemsMobileWrapper";
+
+jest.mock("@headlessui/react", () => {
+ const Dialog = ({ children, className }: any) => (
+
+ {children}
+
+ );
+ const DialogPanel = ({ children, className }: any) => (
+
+ {children}
+
+ );
+ const DialogTitle = ({ children, className }: any) => (
+ {children}
+ );
+ const Transition = ({ show, children }: any) =>
+ show ? {children}
: null;
+ const TransitionChild = ({ children }: any) => {children}
;
-jest.mock('@headlessui/react', () => {
- const Comp = (p: any) => {p.children}
;
return {
- Dialog: Object.assign(Comp, { Panel: Comp, Title: Comp }),
- Transition: { Root: ({ show, children }: any) => (show ? {children}
: null), Child: Comp },
+ Dialog,
+ DialogPanel,
+ DialogTitle,
+ Transition,
+ TransitionChild,
};
});
-describe('CommonDropdownItemsMobileWrapper', () => {
- it('renders when open and closes on button click', () => {
+describe("CommonDropdownItemsMobileWrapper", () => {
+ it("uses the default z-index class when none is provided", () => {
const setOpen = jest.fn();
+
+ render(
+
+ child
+
+ );
+
+ expect(screen.getByTestId("dialog")).toHaveClass("tw-z-[1000]");
+ });
+
+ it("applies a custom z-index class when provided", () => {
+ const setOpen = jest.fn();
+
render(
-
+
child
);
- expect(screen.getByText('test')).toBeInTheDocument();
- fireEvent.click(screen.getByLabelText('Close panel'));
+
+ expect(screen.getByTestId("dialog")).toHaveClass("tw-z-[1020]");
+ });
+
+ it("renders when open and closes on button click", () => {
+ const setOpen = jest.fn();
+
+ render(
+
+ child
+
+ );
+
+ expect(screen.getByText("test")).toBeInTheDocument();
+ fireEvent.click(screen.getByLabelText("Close panel"));
expect(setOpen).toHaveBeenCalledWith(false);
});
});
diff --git a/__tests__/components/waves/drops/WaveDropActionsAddReaction.test.tsx b/__tests__/components/waves/drops/WaveDropActionsAddReaction.test.tsx
index eed39484fe..84e16961d9 100644
--- a/__tests__/components/waves/drops/WaveDropActionsAddReaction.test.tsx
+++ b/__tests__/components/waves/drops/WaveDropActionsAddReaction.test.tsx
@@ -1,5 +1,5 @@
import React from "react";
-import { render, screen, fireEvent, waitFor } from "@testing-library/react";
+import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import WaveDropActionsAddReaction from "@/components/waves/drops/WaveDropActionsAddReaction";
import type { ExtendedDrop } from "@/helpers/waves/drop.helpers";
import { DropSize } from "@/helpers/waves/drop.helpers";
@@ -7,6 +7,14 @@ import { ApiDropType } from "@/generated/models/ApiDropType";
const applyOptimisticDropUpdateMock = jest.fn(() => ({ rollback: jest.fn() }));
const setToastMock = jest.fn();
+const mobileWrapperDialogMock = jest.fn(
+ ({ isOpen, children, zIndexClassName }: any) =>
+ isOpen ? (
+
+ {children}
+
+ ) : null
+);
jest.mock("@/contexts/wave/MyStreamContext", () => ({
useMyStream: jest.fn(() => ({
@@ -39,7 +47,11 @@ jest.mock("@/services/api/common-api", () => ({
commonApiPost: jest.fn(() => Promise.resolve({})),
}));
-// Mock emoji-mart/react Picker and emoji-mart/data
+jest.mock("@/components/mobile-wrapper-dialog/MobileWrapperDialog", () => ({
+ __esModule: true,
+ default: (props: any) => mobileWrapperDialogMock(props),
+}));
+
jest.mock("@emoji-mart/react", () => ({
__esModule: true,
default: ({ onEmojiSelect }: any) => (
@@ -54,7 +66,6 @@ jest.mock("@emoji-mart/data", () => ({
default: {},
}));
-// Mock useEmoji
jest.mock("@/contexts/EmojiContext", () => ({
useEmoji: jest.fn(() => ({
emojiMap: [],
@@ -66,7 +77,6 @@ jest.mock("@/contexts/EmojiContext", () => ({
})),
}));
-// Mock drop object
const baseDrop = {
id: "12345",
wave: { id: "wave-1" },
@@ -92,6 +102,7 @@ const tempDrop = {
stableKey: "temp-001",
stableHash: "hash-temp-001",
} as ExtendedDrop;
+
describe("WaveDropActionsAddReaction", () => {
beforeEach(() => {
jest.clearAllMocks();
@@ -124,7 +135,6 @@ describe("WaveDropActionsAddReaction", () => {
fireEvent.click(button);
expect(await screen.findByTestId("mock-picker")).toBeInTheDocument();
- // Simulate Escape key
fireEvent.keyDown(document, { key: "Escape" });
await waitFor(() => {
expect(screen.queryByTestId("mock-picker")).not.toBeInTheDocument();
@@ -138,7 +148,6 @@ describe("WaveDropActionsAddReaction", () => {
fireEvent.click(button);
expect(await screen.findByTestId("mock-picker")).toBeInTheDocument();
- // Simulate outside click
fireEvent.mouseDown(document.body);
await waitFor(() => {
expect(screen.queryByTestId("mock-picker")).not.toBeInTheDocument();
@@ -171,4 +180,21 @@ describe("WaveDropActionsAddReaction", () => {
fireEvent.click(button);
expect(await screen.findByTestId("mock-picker")).toBeInTheDocument();
});
+
+ it("forwards custom dialog z-index to the mobile wrapper", async () => {
+ render(
+
+ );
+
+ fireEvent.click(screen.getByRole("button", { name: /add reaction/i }));
+
+ expect(await screen.findByTestId("mobile-dialog")).toHaveAttribute(
+ "data-z-index",
+ "tw-z-[1030]"
+ );
+ });
});
diff --git a/__tests__/components/waves/drops/WaveDropMobileMenu.test.tsx b/__tests__/components/waves/drops/WaveDropMobileMenu.test.tsx
index 3da5ea033e..a5765307f1 100644
--- a/__tests__/components/waves/drops/WaveDropMobileMenu.test.tsx
+++ b/__tests__/components/waves/drops/WaveDropMobileMenu.test.tsx
@@ -1,5 +1,6 @@
import { AuthContext } from "@/components/auth/Auth";
import WaveDropMobileMenu from "@/components/waves/drops/WaveDropMobileMenu";
+import { WaveDropLayerProvider } from "@/components/waves/drops/WaveDropLayerContext";
import { ApiDropType } from "@/generated/models/ApiDropType";
import { useDropInteractionRules } from "@/hooks/drops/useDropInteractionRules";
import { render, screen } from "@testing-library/react";
@@ -7,6 +8,19 @@ import userEvent from "@testing-library/user-event";
const mockIsMemesWave = jest.fn();
const writeText = jest.fn().mockResolvedValue(undefined);
+const addReactionMock = jest.fn((props: any) => (
+
+));
+const mobileWrapperMock = jest.fn((props: any) =>
+ props.isOpen ? (
+
+ {props.children}
+
+ ) : null
+);
jest.mock("@/hooks/drops/useDropInteractionRules", () => ({
useDropInteractionRules: jest.fn(),
@@ -30,16 +44,19 @@ jest.mock("@/components/waves/drops/WaveDropActionsMarkUnread", () => () => (
jest.mock("@/components/waves/drops/WaveDropActionsRate", () => () => (
));
-jest.mock("@/components/waves/drops/WaveDropActionsAddReaction", () => () => (
-
-));
+jest.mock("@/components/waves/drops/WaveDropActionsAddReaction", () => ({
+ __esModule: true,
+ default: (props: any) => addReactionMock(props),
+}));
jest.mock("@/components/waves/drops/WaveDropActionsQuickReact", () => () => (
));
jest.mock(
"@/components/utils/select/dropdown/CommonDropdownItemsMobileWrapper",
- () => (props: any) =>
- props.isOpen ? {props.children}
: null
+ () => ({
+ __esModule: true,
+ default: (props: any) => mobileWrapperMock(props),
+ })
);
jest.mock("@/contexts/SeizeSettingsContext", () => ({
@@ -70,6 +87,8 @@ const mockedUseDropInteractionRules = jest.mocked(useDropInteractionRules);
beforeEach(() => {
writeText.mockClear();
+ addReactionMock.mockClear();
+ mobileWrapperMock.mockClear();
mockIsMemesWave.mockReturnValue(false);
mockedUseDropInteractionRules.mockReturnValue({
canShowVote: true,
@@ -107,7 +126,6 @@ test("copies serial jump links for non-memes drops", async () => {
longPressTriggered={false}
setOpen={jest.fn()}
onReply={jest.fn()}
- onQuote={jest.fn()}
onAddReaction={jest.fn()}
/>
@@ -142,7 +160,6 @@ test("copies canonical drop links for memes submissions", async () => {
longPressTriggered={false}
setOpen={jest.fn()}
onReply={jest.fn()}
- onQuote={jest.fn()}
onAddReaction={jest.fn()}
/>
@@ -177,7 +194,6 @@ test("hides follow and clap when author and memes wave", () => {
longPressTriggered={false}
setOpen={jest.fn()}
onReply={jest.fn()}
- onQuote={jest.fn()}
onAddReaction={jest.fn()}
/>
@@ -223,7 +239,6 @@ test("shows pinned-drop action in the mobile menu for admins", () => {
longPressTriggered={false}
setOpen={jest.fn()}
onReply={jest.fn()}
- onQuote={jest.fn()}
onAddReaction={jest.fn()}
/>
@@ -269,7 +284,6 @@ test("does not show pinned-drop action in the mobile menu for non-admins", () =>
longPressTriggered={false}
setOpen={jest.fn()}
onReply={jest.fn()}
- onQuote={jest.fn()}
onAddReaction={jest.fn()}
/>
@@ -358,3 +372,97 @@ test("shows only copy link in the mobile menu for guests", () => {
expect(screen.queryByTestId("set-pinned-drop")).toBeNull();
expect(screen.queryByTestId("delete")).toBeNull();
});
+
+test("uses single-drop layer overrides when provided by context", () => {
+ const drop = {
+ id: "1",
+ serial_no: 1,
+ wave: { id: "w" },
+ drop_type: ApiDropType.Chat,
+ author: { handle: "alice" },
+ } as any;
+
+ render(
+
+
+
+
+
+ );
+
+ expect(screen.getByTestId("wrapper")).toHaveAttribute(
+ "data-z-index",
+ "tw-z-[1020]"
+ );
+ expect(screen.getByTestId("add-reaction")).toHaveAttribute(
+ "data-dialog-z-index",
+ "tw-z-[1030]"
+ );
+});
+
+test("preserves default layer values when context overrides are undefined", () => {
+ const drop = {
+ id: "1",
+ serial_no: 1,
+ wave: { id: "w" },
+ drop_type: ApiDropType.Chat,
+ author: { handle: "alice" },
+ } as any;
+
+ render(
+
+
+
+
+
+ );
+
+ expect(screen.getByTestId("wrapper")).toHaveAttribute(
+ "data-z-index",
+ "tw-z-[1000]"
+ );
+ expect(screen.getByTestId("add-reaction")).toHaveAttribute(
+ "data-dialog-z-index",
+ "tw-z-[1030]"
+ );
+});
diff --git a/components/mobile-wrapper-dialog/MobileWrapperDialog.tsx b/components/mobile-wrapper-dialog/MobileWrapperDialog.tsx
index 8904f30afa..65d312c13a 100644
--- a/components/mobile-wrapper-dialog/MobileWrapperDialog.tsx
+++ b/components/mobile-wrapper-dialog/MobileWrapperDialog.tsx
@@ -107,6 +107,7 @@ export default function MobileWrapperDialog({
showScrollbar,
allowOverflow,
maxWidthClass,
+ zIndexClassName = "tw-z-[1010]",
dismissible = true,
}: {
readonly title?: string | undefined;
@@ -122,6 +123,7 @@ export default function MobileWrapperDialog({
readonly showScrollbar?: boolean | undefined;
readonly allowOverflow?: boolean | undefined;
readonly maxWidthClass?: string | undefined;
+ readonly zIndexClassName?: string | undefined;
readonly dismissible?: boolean | undefined;
}) {
const { isCapacitor, isIos } = useCapacitor();
@@ -163,7 +165,7 @@ export default function MobileWrapperDialog({