This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a commit to branch fix/unsaved-changes-modal-zindex
in repository https://gitbox.apache.org/repos/asf/superset.git

commit ff424c55c8f08aa9d8f6e5b3cdf27f116bc5916a
Author: rusackas <[email protected]>
AuthorDate: Wed Jul 29 00:04:31 2026 -0700

    fix(core): let UnsavedChangesModal use Ant Design's automatic z-index 
stacking
    
    UNSAVED_CHANGES_MODAL_Z_INDEX was a hardcoded literal meant to keep this
    modal above other open modals (e.g. a draggable View query modal).
    Ant Design already auto-increments z-index for every newly opened
    Modal off theme.zIndexPopupBase, so any manual override just needs to
    be bumped again whenever something else's computed z-index creeps
    past it, which is exactly what happened (#42510).
    
    Audited every other zIndex/z-index usage in the frontend: this was the
    only place hardcoding a z-index override on a Modal-class component.
    Everything else either doesn't need one, or already derives it from
    theme.zIndexPopupBase/theme.zIndexBase for a real, local reason (sticky
    headers inside modal content, the native Fullscreen API's interaction
    with portaled antd components, custom fixed-position overlays that sit
    outside Ant Design's own stacking system entirely). None of those are
    part of this bug and are left untouched.
    
    Since this modal is always opened on top of whatever it's interrupting,
    dropping the override (the constant, the prop, and the pass-through)
    lets it stack correctly with no number to maintain.
---
 .../UnsavedChangesModal.test.tsx                   | 37 ++++++++++++++++++++++
 .../src/components/UnsavedChangesModal/index.tsx   |  7 ----
 2 files changed, 37 insertions(+), 7 deletions(-)

diff --git 
a/superset-frontend/packages/superset-ui-core/src/components/UnsavedChangesModal/UnsavedChangesModal.test.tsx
 
b/superset-frontend/packages/superset-ui-core/src/components/UnsavedChangesModal/UnsavedChangesModal.test.tsx
index 995bd704d6a..fac9a2b1bb2 100644
--- 
a/superset-frontend/packages/superset-ui-core/src/components/UnsavedChangesModal/UnsavedChangesModal.test.tsx
+++ 
b/superset-frontend/packages/superset-ui-core/src/components/UnsavedChangesModal/UnsavedChangesModal.test.tsx
@@ -17,6 +17,7 @@
  * under the License.
  */
 import { render, screen, userEvent } from '@superset-ui/core/spec';
+import { Modal } from '@superset-ui/core/components';
 import { UnsavedChangesModal } from '.';
 
 test('should render nothing if showModal is false', () => {
@@ -94,3 +95,39 @@ test('should only call handleSave when clicking the Save 
button', async () => {
   expect(mockOnHide).not.toHaveBeenCalled();
   expect(mockOnConfirmNavigation).not.toHaveBeenCalled();
 });
+
+test('renders above an already-open modal without a hardcoded z-index', () => {
+  // Regression test for a bug where this modal could render BEHIND another
+  // already-open modal (e.g. a draggable "View query" modal), because its
+  // z-index was pinned to a hardcoded constant instead of relying on Ant
+  // Design's automatic z-index stacking. Since this modal is always opened
+  // on top of whatever it's interrupting, it should always come out ahead
+  // with no manual override at all.
+  render(
+    <>
+      <Modal show title="Other open modal" onHide={() => {}}>
+        <div>Other modal content</div>
+      </Modal>
+      <UnsavedChangesModal
+        showModal
+        onHide={() => {}}
+        handleSave={() => {}}
+        onConfirmNavigation={() => {}}
+      />
+    </>,
+  );
+
+  const otherDialog = screen.getByRole('dialog', {
+    name: /other open modal/i,
+  });
+  const unsavedChangesDialog = screen.getByRole('dialog', {
+    name: /unsaved changes/i,
+  });
+
+  const otherZIndex = Number(getComputedStyle(otherDialog).zIndex);
+  const unsavedChangesZIndex = Number(
+    getComputedStyle(unsavedChangesDialog).zIndex,
+  );
+
+  expect(unsavedChangesZIndex).toBeGreaterThan(otherZIndex);
+});
diff --git 
a/superset-frontend/packages/superset-ui-core/src/components/UnsavedChangesModal/index.tsx
 
b/superset-frontend/packages/superset-ui-core/src/components/UnsavedChangesModal/index.tsx
index bfbbfc8a17d..cca9c7573df 100644
--- 
a/superset-frontend/packages/superset-ui-core/src/components/UnsavedChangesModal/index.tsx
+++ 
b/superset-frontend/packages/superset-ui-core/src/components/UnsavedChangesModal/index.tsx
@@ -20,10 +20,6 @@ import { t } from '@apache-superset/core/translation';
 import { Icons, Modal, Typography, Button } from 
'@superset-ui/core/components';
 import type { FC, ReactElement } from 'react';
 
-// Ant Design's default modal zIndex is 1000. Using a higher value ensures
-// this dialog always renders above other open modals (e.g. a draggable View 
SQL modal).
-const UNSAVED_CHANGES_MODAL_Z_INDEX = 1100;
-
 export type UnsavedChangesModalProps = {
   showModal: boolean;
   onHide: () => void;
@@ -31,7 +27,6 @@ export type UnsavedChangesModalProps = {
   onConfirmNavigation: () => void;
   title?: string;
   body?: string;
-  zIndex?: number;
 };
 
 export const UnsavedChangesModal: FC<UnsavedChangesModalProps> = ({
@@ -41,7 +36,6 @@ export const UnsavedChangesModal: 
FC<UnsavedChangesModalProps> = ({
   onConfirmNavigation,
   title = 'Unsaved Changes',
   body = "If you don't save, changes will be lost.",
-  zIndex = UNSAVED_CHANGES_MODAL_Z_INDEX,
 }: UnsavedChangesModalProps): ReactElement => (
   <Modal
     centered
@@ -49,7 +43,6 @@ export const UnsavedChangesModal: 
FC<UnsavedChangesModalProps> = ({
     onHide={onHide}
     show={showModal}
     width="444px"
-    zIndex={zIndex}
     title={
       <>
         <Icons.WarningOutlined iconSize="m" style={{ marginRight: 8 }} />

Reply via email to