You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by mi...@apache.org on 2026/04/28 12:19:09 UTC
(superset) branch 6.1 updated (4db4ac41c4f -> 7c885e5a196)
This is an automated email from the ASF dual-hosted git repository.
michael-s-molina pushed a change to branch 6.1
in repository https://gitbox.apache.org/repos/asf/superset.git
from 4db4ac41c4f fix(sqllab): explore to chart is disabled (#39630)
new 61bcf578a69 fix(explore): ensure unsaved-changes dialog renders above View SQL modal (#39569)
new 87e5450cbee fix(table chart): fix rerender bug that continuously cleared search box (#39707)
new b55bb4e3ce6 fix(query-history): enable sorting by Duration column (#39637)
new 7c885e5a196 fix(theme): set color-scheme on html to fix dark mode scrollbars (#39704)
The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../superset-core/src/theme/GlobalStyles.tsx | 6 +
.../src/components/UnsavedChangesModal/index.tsx | 7 +
.../plugin-chart-table/src/DataTable/DataTable.tsx | 21 +-
.../plugin-chart-table/test/TableChart.test.tsx | 218 +++++++++++++++++++++
superset-frontend/src/pages/Chart/Chart.test.tsx | 21 +-
superset/models/sql_lab.py | 15 ++
superset/queries/api.py | 1 +
superset/queries/schemas.py | 1 +
tests/integration_tests/queries/api_tests.py | 80 ++++++++
9 files changed, 358 insertions(+), 12 deletions(-)
(superset) 02/04: fix(table chart): fix rerender bug that continuously cleared search box (#39707)
Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
michael-s-molina pushed a commit to branch 6.1
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 87e5450cbeeacd998debbe5433eeb15399789138
Author: Sam Firke <sf...@users.noreply.github.com>
AuthorDate: Tue Apr 28 07:40:56 2026 -0400
fix(table chart): fix rerender bug that continuously cleared search box (#39707)
(cherry picked from commit 3395620b6ee56cb52a919943e5f1c72b09d61b81)
---
.../plugin-chart-table/src/DataTable/DataTable.tsx | 21 +-
.../plugin-chart-table/test/TableChart.test.tsx | 218 +++++++++++++++++++++
superset-frontend/src/pages/Chart/Chart.test.tsx | 21 +-
3 files changed, 248 insertions(+), 12 deletions(-)
diff --git a/superset-frontend/plugins/plugin-chart-table/src/DataTable/DataTable.tsx b/superset-frontend/plugins/plugin-chart-table/src/DataTable/DataTable.tsx
index 74ffcbefb1f..4cec527e8cd 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/DataTable/DataTable.tsx
+++ b/superset-frontend/plugins/plugin-chart-table/src/DataTable/DataTable.tsx
@@ -147,7 +147,25 @@ export default typedMemo(function DataTable<D extends object>({
hooks || [],
].flat();
- const columnNames = Object.keys(data?.[0] || {});
+ const columnNames = columns.map((column, index) => {
+ const normalizedColumn = column as typeof column & {
+ accessor?: string | ((row: D) => unknown);
+ columnKey?: string;
+ id?: string;
+ };
+
+ const accessorName =
+ typeof normalizedColumn.accessor === 'string'
+ ? normalizedColumn.accessor
+ : undefined;
+
+ return (
+ normalizedColumn.columnKey ??
+ normalizedColumn.id ??
+ accessorName ??
+ String(index)
+ );
+ });
const previousColumnNames = usePrevious(columnNames);
const resultsSize = serverPagination ? rowCount : data.length;
const sortByRef = useRef([]); // cache initial `sortby` so sorting doesn't trigger page reset
@@ -237,6 +255,7 @@ export default typedMemo(function DataTable<D extends object>({
getTableSize: defaultGetTableSize,
globalFilter: defaultGlobalFilter,
sortTypes,
+ autoResetGlobalFilter: !isEqual(columnNames, previousColumnNames),
autoResetSortBy: !isEqual(columnNames, previousColumnNames),
manualSortBy: !!serverPagination,
...moreUseTableOptions,
diff --git a/superset-frontend/plugins/plugin-chart-table/test/TableChart.test.tsx b/superset-frontend/plugins/plugin-chart-table/test/TableChart.test.tsx
index 6988f9afc18..a043c67b39f 100644
--- a/superset-frontend/plugins/plugin-chart-table/test/TableChart.test.tsx
+++ b/superset-frontend/plugins/plugin-chart-table/test/TableChart.test.tsx
@@ -36,6 +36,8 @@ import {
SMART_DATE_ID,
getTimeFormatterForGranularity,
} from '@superset-ui/core';
+import { CellProps, Column, HeaderProps } from 'react-table';
+import DataTable from '../src/DataTable/DataTable';
import TableChart, { sanitizeHeaderId } from '../src/TableChart';
import { GenericDataType } from '@apache-superset/core/common';
import transformProps from '../src/transformProps';
@@ -1980,6 +1982,222 @@ describe('plugin-chart-table', () => {
expect(totalCellAfter).toBeInTheDocument();
});
});
+
+ test('preserves client-side search text across temporal table rerenders', async () => {
+ const formDataWithSearch = {
+ ...testData.basic.formData,
+ include_search: true,
+ server_pagination: false,
+ };
+
+ const renderChart = () => {
+ const props = transformProps({
+ ...testData.basic,
+ formData: formDataWithSearch,
+ });
+ props.includeSearch = true;
+
+ return (
+ <ProviderWrapper>
+ <TableChart {...props} sticky={false} />
+ </ProviderWrapper>
+ );
+ };
+
+ const { rerender } = render(renderChart());
+
+ const searchInput = screen.getByRole('textbox');
+ fireEvent.change(searchInput, { target: { value: 'Michael' } });
+
+ await waitFor(() => {
+ expect(searchInput).toHaveValue('Michael');
+ expect(screen.getByText('Michael')).toBeInTheDocument();
+ });
+
+ rerender(renderChart());
+
+ await waitFor(() => {
+ expect(screen.getByRole('textbox')).toHaveValue('Michael');
+ expect(screen.getByText('Michael')).toBeInTheDocument();
+ });
+ });
+
+ test('preserves client-side search text when rerendered with empty data', async () => {
+ const formDataWithSearch = {
+ ...testData.basic.formData,
+ include_search: true,
+ server_pagination: false,
+ };
+
+ const renderChart = (data = testData.basic.queriesData[0].data) => {
+ const props = transformProps({
+ ...testData.basic,
+ formData: formDataWithSearch,
+ queriesData: [
+ {
+ ...testData.basic.queriesData[0],
+ data,
+ },
+ ],
+ });
+ props.includeSearch = true;
+
+ return (
+ <ProviderWrapper>
+ <TableChart {...props} sticky={false} />
+ </ProviderWrapper>
+ );
+ };
+
+ const { rerender } = render(renderChart());
+
+ const searchInput = screen.getByRole('textbox');
+ fireEvent.change(searchInput, { target: { value: 'Michael' } });
+
+ await waitFor(() => {
+ expect(searchInput).toHaveValue('Michael');
+ expect(screen.getByText('Michael')).toBeInTheDocument();
+ });
+
+ rerender(renderChart([]));
+
+ await waitFor(() => {
+ expect(screen.getByRole('textbox')).toHaveValue('Michael');
+ expect(screen.getByLabelText('Search 0 records')).toHaveValue(
+ 'Michael',
+ );
+ });
+ });
+
+ test('preserves client-side search text for function accessor columns', async () => {
+ type DataRow = {
+ city: string;
+ firstName: string;
+ };
+
+ const makeColumns = (): Column<DataRow>[] => [
+ {
+ Header: ({ column }: HeaderProps<DataRow>) => (
+ <th data-column-name={column.id}>First name</th>
+ ),
+ Cell: ({ value }: CellProps<DataRow>) => <td>{value}</td>,
+ id: 'firstName',
+ accessor: ((row: DataRow) => row.firstName) as never,
+ },
+ {
+ Header: ({ column }: HeaderProps<DataRow>) => (
+ <th data-column-name={column.id}>City</th>
+ ),
+ Cell: ({ value }: CellProps<DataRow>) => <td>{value}</td>,
+ id: 'city',
+ accessor: ((row: DataRow) => row.city) as never,
+ },
+ ];
+
+ const data: DataRow[] = [
+ { firstName: 'Michael', city: 'Paris' },
+ { firstName: 'Jordan', city: 'London' },
+ ];
+
+ const renderDataTable = () => (
+ <ProviderWrapper>
+ <DataTable<DataRow>
+ columns={makeColumns()}
+ data={data}
+ rowCount={data.length}
+ serverPagination={false}
+ serverPaginationData={{}}
+ onServerPaginationChange={jest.fn()}
+ handleSortByChange={jest.fn()}
+ sortByFromParent={[]}
+ onSearchColChange={jest.fn()}
+ searchOptions={[]}
+ sticky={false}
+ />
+ </ProviderWrapper>
+ );
+
+ const { rerender } = render(renderDataTable());
+
+ const searchInput = screen.getByRole('textbox');
+ fireEvent.change(searchInput, { target: { value: 'Michael' } });
+
+ await waitFor(() => {
+ expect(searchInput).toHaveValue('Michael');
+ expect(screen.getByText('Michael')).toBeInTheDocument();
+ });
+
+ rerender(renderDataTable());
+
+ await waitFor(() => {
+ expect(screen.getByRole('textbox')).toHaveValue('Michael');
+ expect(screen.getByText('Michael')).toBeInTheDocument();
+ });
+ });
+
+ test('preserves client-side search text for string accessor columns without ids', async () => {
+ type DataRow = {
+ city: string;
+ firstName: string;
+ };
+
+ const makeColumns = (): Column<DataRow>[] => [
+ {
+ Header: ({ column }: HeaderProps<DataRow>) => (
+ <th data-column-name={column.id}>First name</th>
+ ),
+ Cell: ({ value }: CellProps<DataRow>) => <td>{value}</td>,
+ accessor: 'firstName',
+ },
+ {
+ Header: ({ column }: HeaderProps<DataRow>) => (
+ <th data-column-name={column.id}>City</th>
+ ),
+ Cell: ({ value }: CellProps<DataRow>) => <td>{value}</td>,
+ accessor: 'city',
+ },
+ ];
+
+ const data: DataRow[] = [
+ { firstName: 'Michael', city: 'Paris' },
+ { firstName: 'Jordan', city: 'London' },
+ ];
+
+ const renderDataTable = () => (
+ <ProviderWrapper>
+ <DataTable<DataRow>
+ columns={makeColumns()}
+ data={data}
+ rowCount={data.length}
+ serverPagination={false}
+ serverPaginationData={{}}
+ onServerPaginationChange={jest.fn()}
+ handleSortByChange={jest.fn()}
+ sortByFromParent={[]}
+ onSearchColChange={jest.fn()}
+ searchOptions={[]}
+ sticky={false}
+ />
+ </ProviderWrapper>
+ );
+
+ const { rerender } = render(renderDataTable());
+
+ const searchInput = screen.getByRole('textbox');
+ fireEvent.change(searchInput, { target: { value: 'Michael' } });
+
+ await waitFor(() => {
+ expect(searchInput).toHaveValue('Michael');
+ expect(screen.getByText('Michael')).toBeInTheDocument();
+ });
+
+ rerender(renderDataTable());
+
+ await waitFor(() => {
+ expect(screen.getByRole('textbox')).toHaveValue('Michael');
+ expect(screen.getByText('Michael')).toBeInTheDocument();
+ });
+ });
});
test('should build columnLabelToNameMap for adhoc columns with custom labels', () => {
diff --git a/superset-frontend/src/pages/Chart/Chart.test.tsx b/superset-frontend/src/pages/Chart/Chart.test.tsx
index b80ccc32b29..6f15faceb99 100644
--- a/superset-frontend/src/pages/Chart/Chart.test.tsx
+++ b/superset-frontend/src/pages/Chart/Chart.test.tsx
@@ -405,7 +405,8 @@ describe('ChartPage', () => {
rejectFirstRequest = reject;
});
- fetchMock.get(exploreApiRoute, () => firstRequestPromise);
+ const firstRequestHandler = jest.fn(() => firstRequestPromise);
+ fetchMock.get(exploreApiRoute, firstRequestHandler);
render(
<>
@@ -419,16 +420,16 @@ describe('ChartPage', () => {
},
);
- // Wait for the first request to be initiated
- await waitFor(() =>
- expect(fetchMock.callHistory.calls(exploreApiRoute).length).toBe(1),
- );
+ // Wait for the initial request cycle to begin. Under CI, mount/navigation
+ // setup can trigger more than one explore fetch before history is cleared.
+ await waitFor(() => expect(firstRequestHandler).toHaveBeenCalled());
// Set up second request to return immediately
fetchMock.clearHistory().removeRoutes();
- fetchMock.get(exploreApiRoute, {
+ const secondRequestHandler = jest.fn(() => ({
result: { dataset: { id: 1 }, form_data: exploreFormData },
- });
+ }));
+ fetchMock.get(exploreApiRoute, secondRequestHandler);
// Navigate to trigger a new request (which should abort the first)
fireEvent.click(screen.getByText('Navigate'));
@@ -441,10 +442,8 @@ describe('ChartPage', () => {
// Wait for the first request to settle before asserting
await firstRequestPromise.catch(() => undefined);
- // Wait for the second request to complete
- await waitFor(() =>
- expect(fetchMock.callHistory.calls(exploreApiRoute).length).toBe(1),
- );
+ // Wait for the replacement request to run after navigation.
+ await waitFor(() => expect(secondRequestHandler).toHaveBeenCalled());
// No error toast should be shown from the aborted first request
expect(addDangerToastSpy).not.toHaveBeenCalled();
(superset) 04/04: fix(theme): set color-scheme on html to fix dark mode scrollbars (#39704)
Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
michael-s-molina pushed a commit to branch 6.1
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 7c885e5a1968ae40a54b8576a74a3e902c86612b
Author: Michael S. Molina <70...@users.noreply.github.com>
AuthorDate: Tue Apr 28 09:17:11 2026 -0300
fix(theme): set color-scheme on html to fix dark mode scrollbars (#39704)
Co-authored-by: Claude Sonnet 4.6 <no...@anthropic.com>
(cherry picked from commit 7bee2afa8e43372c929fe9640cb0b9be0646e99f)
---
superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx b/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx
index 33a30a9fa61..7c750d2087f 100644
--- a/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx
+++ b/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx
@@ -29,14 +29,20 @@ import '@fontsource/ibm-plex-mono/600.css';
/* eslint-enable import/extensions */
import { css, useTheme, Global } from '@emotion/react';
+import { useThemeMode } from './utils/themeUtils';
export const GlobalStyles = () => {
const theme = useTheme();
+ const isDark = useThemeMode();
return (
<Global
key={`global-${theme.colorLink}`}
styles={css`
// SPA
+ html {
+ color-scheme: ${isDark ? 'dark' : 'light'};
+ }
+
html,
body,
#app {
(superset) 03/04: fix(query-history): enable sorting by Duration column (#39637)
Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
michael-s-molina pushed a commit to branch 6.1
in repository https://gitbox.apache.org/repos/asf/superset.git
commit b55bb4e3ce612cd60c126f7873c6692bc6fded8e
Author: Michael S. Molina <70...@users.noreply.github.com>
AuthorDate: Tue Apr 28 08:49:58 2026 -0300
fix(query-history): enable sorting by Duration column (#39637)
Co-authored-by: Claude Sonnet 4.6 <no...@anthropic.com>
(cherry picked from commit c4a8b34b11f0ef4216282ab52aa35c02072818a8)
---
superset/models/sql_lab.py | 15 ++++++
superset/queries/api.py | 1 +
superset/queries/schemas.py | 1 +
tests/integration_tests/queries/api_tests.py | 80 ++++++++++++++++++++++++++++
4 files changed, 97 insertions(+)
diff --git a/superset/models/sql_lab.py b/superset/models/sql_lab.py
index 7d303ffa99a..382b272af42 100644
--- a/superset/models/sql_lab.py
+++ b/superset/models/sql_lab.py
@@ -44,6 +44,7 @@ from sqlalchemy import (
Text,
)
from sqlalchemy.engine.url import URL
+from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import backref, relationship
from sqlalchemy.sql.elements import ColumnElement, literal_column
from superset_core.queries.models import (
@@ -159,6 +160,20 @@ class Query(
DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=True
)
+ @hybrid_property
+ def duration(self) -> Optional[float]:
+ start = self.start_running_time or self.start_time
+ if self.end_time is not None and start is not None:
+ return float(self.end_time - start)
+ return None
+
+ @duration.expression # type: ignore[no-redef]
+ def duration(cls) -> ColumnElement: # noqa: N805
+ return sqla.func.coalesce(
+ cls.end_time - sqla.func.coalesce(cls.start_running_time, cls.start_time),
+ 0,
+ )
+
database = relationship(
"Database",
foreign_keys=[database_id],
diff --git a/superset/queries/api.py b/superset/queries/api.py
index 3b4b1e632f1..a1e9627738b 100644
--- a/superset/queries/api.py
+++ b/superset/queries/api.py
@@ -136,6 +136,7 @@ class QueryRestApi(BaseSupersetModelRestApi):
order_columns = [
"changed_on",
"database.database_name",
+ "duration",
"rows",
"schema",
"start_time",
diff --git a/superset/queries/schemas.py b/superset/queries/schemas.py
index b66d0593667..589dc271910 100644
--- a/superset/queries/schemas.py
+++ b/superset/queries/schemas.py
@@ -60,6 +60,7 @@ class QuerySchema(Schema):
schema = fields.String()
sql = fields.String()
sql_tables = fields.Method("get_sql_tables")
+ start_running_time = fields.Float(attribute="start_running_time")
start_time = fields.Float(attribute="start_time")
status = fields.String()
tab_name = fields.String()
diff --git a/tests/integration_tests/queries/api_tests.py b/tests/integration_tests/queries/api_tests.py
index db7b6b825e1..a4ed1ddc697 100644
--- a/tests/integration_tests/queries/api_tests.py
+++ b/tests/integration_tests/queries/api_tests.py
@@ -54,6 +54,9 @@ class TestQueryApi(SupersetTestCase):
tab_name: str = "",
status: str = "success",
changed_on: datetime = datetime(2020, 1, 1),
+ start_time: float | None = None,
+ start_running_time: float | None = None,
+ end_time: float | None = None,
) -> Query:
database = db.session.query(Database).get(database_id)
user = db.session.query(security_manager.user_model).get(user_id)
@@ -70,6 +73,9 @@ class TestQueryApi(SupersetTestCase):
tab_name=tab_name,
status=status,
changed_on=changed_on,
+ start_time=start_time,
+ start_running_time=start_running_time,
+ end_time=end_time,
)
db.session.add(query)
db.session.commit()
@@ -275,6 +281,7 @@ class TestQueryApi(SupersetTestCase):
"schema",
"sql",
"sql_tables",
+ "start_running_time",
"start_time",
"status",
"tab_name",
@@ -361,6 +368,7 @@ class TestQueryApi(SupersetTestCase):
order_columns = [
"changed_on",
"database.database_name",
+ "duration",
"rows",
"schema",
"sql",
@@ -374,6 +382,78 @@ class TestQueryApi(SupersetTestCase):
rv = self.client.get(uri)
assert rv.status_code == 200
+ def test_get_list_query_order_duration(self):
+ """
+ Query API: Test that sorting by duration orders by end_time - start_time,
+ falling back to start_time when start_running_time is absent, and treating
+ NULL durations (no end_time) as zero.
+ """
+ admin = self.get_user("admin")
+ example_db = get_example_database()
+ base_time = 1_000_000.0
+
+ # duration = 0.031 (uses start_running_time as start)
+ q_long = self.insert_query(
+ example_db.id,
+ admin.id,
+ self.get_random_string(),
+ start_time=base_time,
+ start_running_time=base_time + 0.005,
+ end_time=base_time + 0.036,
+ )
+ # duration = 0.021
+ q_medium = self.insert_query(
+ example_db.id,
+ admin.id,
+ self.get_random_string(),
+ start_time=base_time,
+ start_running_time=None,
+ end_time=base_time + 0.021,
+ )
+ # duration = 0 (no end_time, NULL treated as 0)
+ q_null = self.insert_query(
+ example_db.id,
+ admin.id,
+ self.get_random_string(),
+ start_time=base_time,
+ start_running_time=None,
+ end_time=None,
+ )
+
+ # Use a unique sql_editor_id to isolate these test queries
+ test_editor_id = self.get_random_string()
+ q_long.sql_editor_id = test_editor_id
+ q_medium.sql_editor_id = test_editor_id
+ q_null.sql_editor_id = test_editor_id
+ db.session.commit()
+
+ self.login(ADMIN_USERNAME)
+ arguments = {
+ "order_column": "duration",
+ "order_direction": "asc",
+ "filters": [{"col": "sql_editor_id", "opr": "eq", "value": test_editor_id}],
+ }
+ uri = f"api/v1/query/?q={rison.dumps(arguments)}"
+ rv = self.client.get(uri)
+ assert rv.status_code == 200
+ data = rv.get_json()
+ ids = [r["id"] for r in data["result"]]
+ assert ids == [q_null.id, q_medium.id, q_long.id]
+
+ # descending should be the reverse
+ arguments["order_direction"] = "desc"
+ uri = f"api/v1/query/?q={rison.dumps(arguments)}"
+ rv = self.client.get(uri)
+ assert rv.status_code == 200
+ data = rv.get_json()
+ ids = [r["id"] for r in data["result"]]
+ assert ids == [q_long.id, q_medium.id, q_null.id]
+
+ db.session.delete(q_long)
+ db.session.delete(q_medium)
+ db.session.delete(q_null)
+ db.session.commit()
+
def test_get_list_query_no_data_access(self):
"""
Query API: Test get queries no data access
(superset) 01/04: fix(explore): ensure unsaved-changes dialog renders above View SQL modal (#39569)
Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
michael-s-molina pushed a commit to branch 6.1
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 61bcf578a693cf1a331bbe8750afcdfdf6dbaad3
Author: Evan Rusackas <ev...@preset.io>
AuthorDate: Mon Apr 27 13:02:25 2026 -0400
fix(explore): ensure unsaved-changes dialog renders above View SQL modal (#39569)
Co-authored-by: yousoph <so...@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <no...@anthropic.com>
(cherry picked from commit 7c4b2b137c7edf512822f9c0c753d0e7920b7b23)
---
.../superset-ui-core/src/components/UnsavedChangesModal/index.tsx | 7 +++++++
1 file changed, 7 insertions(+)
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 cca9c7573df..bfbbfc8a17d 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,6 +20,10 @@ 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;
@@ -27,6 +31,7 @@ export type UnsavedChangesModalProps = {
onConfirmNavigation: () => void;
title?: string;
body?: string;
+ zIndex?: number;
};
export const UnsavedChangesModal: FC<UnsavedChangesModalProps> = ({
@@ -36,6 +41,7 @@ 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
@@ -43,6 +49,7 @@ export const UnsavedChangesModal: FC<UnsavedChangesModalProps> = ({
onHide={onHide}
show={showModal}
width="444px"
+ zIndex={zIndex}
title={
<>
<Icons.WarningOutlined iconSize="m" style={{ marginRight: 8 }} />