diff --git a/package.json b/package.json
index b92c49f..bb69a5f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@internxt/ui",
- "version": "0.1.27",
+ "version": "0.1.28",
"description": "Library of Internxt components",
"repository": {
"type": "git",
diff --git a/src/components/overlay/modal/Modal.tsx b/src/components/overlay/modal/Modal.tsx
index d5d9921..8a05974 100644
--- a/src/components/overlay/modal/Modal.tsx
+++ b/src/components/overlay/modal/Modal.tsx
@@ -104,7 +104,10 @@ const Modal = ({
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
- if (modalRef.current && !modalRef.current.contains(event.target as Node) && !preventClosing) {
+ const modal = modalRef.current;
+ const isClickOutsideModal = !!modal && !event.composedPath().includes(modal);
+
+ if (isClickOutsideModal && !preventClosing) {
event.preventDefault();
closeLastOpenModal();
}
diff --git a/src/components/overlay/modal/__test__/Modal.test.tsx b/src/components/overlay/modal/__test__/Modal.test.tsx
index de90eac..c54b97d 100644
--- a/src/components/overlay/modal/__test__/Modal.test.tsx
+++ b/src/components/overlay/modal/__test__/Modal.test.tsx
@@ -1,8 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
-import { describe, it, afterEach, expect, vi } from 'vitest';
+import { flushSync } from 'react-dom';
+import { describe, it, afterEach, beforeEach, expect, vi } from 'vitest';
import { Modal } from '../';
+import { Input } from '../../../input/input';
import { afterAll, beforeAll } from 'vitest';
describe('Modal Component', () => {
@@ -101,6 +103,43 @@ describe('Modal Component', () => {
expect(onCloseMock).toHaveBeenCalledTimes(1);
});
+ describe('when the clicked element is detached by a re-render before the document listener runs', () => {
+ const flushPendingRender = () => flushSync(() => {});
+
+ beforeEach(() => {
+ document.body.addEventListener('mousedown', flushPendingRender);
+ });
+
+ afterEach(() => {
+ document.body.removeEventListener('mousedown', flushPendingRender);
+ });
+
+ it('should not call onClose when toggling the password visibility of an input inside the modal', () => {
+ render(
+
+
+ ,
+ );
+ const passwordInput = screen.getByPlaceholderText('Password');
+ fireEvent.focus(passwordInput);
+ const eyeIcon = screen.getByRole('button').querySelector('svg') as SVGElement;
+
+ fireEvent.mouseDown(eyeIcon);
+
+ expect(eyeIcon.isConnected).toBe(false);
+ expect(passwordInput).toHaveAttribute('type', 'text');
+ expect(onCloseMock).not.toHaveBeenCalled();
+ });
+
+ it('should still call onClose when the mousedown happens outside the modal', () => {
+ renderModal();
+
+ fireEvent.mouseDown(document.body);
+
+ expect(onCloseMock).toHaveBeenCalledTimes(1);
+ });
+ });
+
it('should not call onClose when click outside if preventClosing is true', () => {
render(