diff --git a/frontend/src/metabase/admin/datamodel/metadata/components/MetadataEditor/MetadataEditor.unit.spec.tsx b/frontend/src/metabase/admin/datamodel/metadata/components/MetadataEditor/MetadataEditor.unit.spec.tsx index e8226072b91ca0c5a73eb64c8200bd9963cb1823..fbf416f3ad9e709657fa0eca80567804b28e8f0e 100644 --- a/frontend/src/metabase/admin/datamodel/metadata/components/MetadataEditor/MetadataEditor.unit.spec.tsx +++ b/frontend/src/metabase/admin/datamodel/metadata/components/MetadataEditor/MetadataEditor.unit.spec.tsx @@ -1,4 +1,4 @@ -import { Route } from "react-router"; +import { Link, Route } from "react-router"; import userEvent from "@testing-library/user-event"; import { within } from "@testing-library/react"; import type { Database } from "metabase-types/api"; @@ -109,18 +109,36 @@ const JSON_DB = createMockDatabase({ interface SetupOpts { databases?: Database[]; + initialRoute?: string; } -const setup = async ({ databases = [SAMPLE_DB] }: SetupOpts = {}) => { +const setup = async ({ + databases = [SAMPLE_DB], + initialRoute = "admin/datamodel", +}: SetupOpts = {}) => { setupDatabasesEndpoints(databases); setupSearchEndpoints([]); - renderWithProviders( - <Route path="admin/datamodel">{getMetadataRoutes()}</Route>, - { withRouter: true, initialRoute: "admin/datamodel" }, + const OtherComponent = () => { + return ( + <> + <span>Another route</span> + <Link to="admin/datamodel">Link to Datamodel</Link> + </> + ); + }; + + const { history } = renderWithProviders( + <> + <Route path="notAdmin" component={OtherComponent} /> + <Route path="admin/datamodel">{getMetadataRoutes()}</Route> + </>, + { withRouter: true, initialRoute }, ); await waitForLoaderToBeRemoved(); + + return { history }; }; describe("MetadataEditor", () => { @@ -475,4 +493,20 @@ describe("MetadataEditor", () => { expect(within(section).getByText("JSON.version")).toBeInTheDocument(); }); }); + + describe("navigation", () => { + it("should replace locations in history stack when being routed automatically", async () => { + const { history } = await setup({ initialRoute: "notAdmin" }); + + expect(screen.getByText("Link to Datamodel")).toBeInTheDocument(); + userEvent.click(screen.getByText("Link to Datamodel")); + + await waitForLoaderToBeRemoved(); + expect(screen.getByText("Sample Database")).toBeInTheDocument(); + + history?.goBack(); + + expect(screen.getByText("Link to Datamodel")).toBeInTheDocument(); + }); + }); }); diff --git a/frontend/src/metabase/admin/datamodel/metadata/components/MetadataHeader/MetadataHeader.tsx b/frontend/src/metabase/admin/datamodel/metadata/components/MetadataHeader/MetadataHeader.tsx index 344912feabe0947a5f004f97a03fe83ab644aded..a09ec1a16b0faae5e56081018d947ef1b836bb03 100644 --- a/frontend/src/metabase/admin/datamodel/metadata/components/MetadataHeader/MetadataHeader.tsx +++ b/frontend/src/metabase/admin/datamodel/metadata/components/MetadataHeader/MetadataHeader.tsx @@ -1,6 +1,6 @@ import { useLayoutEffect } from "react"; import { connect } from "react-redux"; -import { push } from "react-router-redux"; +import { push, replace } from "react-router-redux"; import { t } from "ttag"; import _ from "underscore"; import { PLUGIN_FEATURE_LEVEL_PERMISSIONS } from "metabase/plugins"; @@ -24,12 +24,20 @@ interface DatabaseLoaderProps { } interface DispatchProps { - onSelectDatabase: (databaseId: DatabaseId) => void; + onSelectDatabase: ( + databaseId: DatabaseId, + options: { useReplace?: boolean }, + ) => void; } const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ - onSelectDatabase: databaseId => - dispatch(push(Urls.dataModelDatabase(databaseId))), + // When navigating programatically, use replace so that the browser back button works + onSelectDatabase: (databaseId, { useReplace = false } = {}) => + dispatch( + useReplace + ? replace(Urls.dataModelDatabase(databaseId)) + : push(Urls.dataModelDatabase(databaseId)), + ), }); type MetadataHeaderProps = OwnProps & DatabaseLoaderProps & DispatchProps; @@ -43,7 +51,7 @@ const MetadataHeader = ({ }: MetadataHeaderProps) => { useLayoutEffect(() => { if (databases.length > 0 && selectedDatabaseId == null) { - onSelectDatabase(databases[0].id); + onSelectDatabase(databases[0].id, { useReplace: true }); } }, [databases, selectedDatabaseId, onSelectDatabase]); diff --git a/frontend/src/metabase/admin/datamodel/metadata/components/MetadataSchemaList/MetadataSchemaList.tsx b/frontend/src/metabase/admin/datamodel/metadata/components/MetadataSchemaList/MetadataSchemaList.tsx index 97cfbbbf4f4428b5f4759edcb6fc4ac18ada5d84..08ca494976181de2f626f6ffd6e447837a17067d 100644 --- a/frontend/src/metabase/admin/datamodel/metadata/components/MetadataSchemaList/MetadataSchemaList.tsx +++ b/frontend/src/metabase/admin/datamodel/metadata/components/MetadataSchemaList/MetadataSchemaList.tsx @@ -1,6 +1,6 @@ import { useCallback, useLayoutEffect, useMemo, useState } from "react"; import { connect } from "react-redux"; -import { push } from "react-router-redux"; +import { push, replace } from "react-router-redux"; import cx from "classnames"; import { msgid, ngettext, t } from "ttag"; import _ from "underscore"; @@ -22,14 +22,24 @@ interface SchemaLoaderProps { } interface DispatchProps { - onSelectSchema: (databaseId: DatabaseId, schemaId: SchemaId) => void; + onSelectSchema: ( + databaseId: DatabaseId, + schemaId: SchemaId, + options?: { useReplace?: boolean }, + ) => void; } type MetadataSchemaListProps = OwnProps & SchemaLoaderProps & DispatchProps; const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ - onSelectSchema: (databaseId, schemaId) => - dispatch(push(Urls.dataModelSchema(databaseId, schemaId))), + // When navigating programatically, use replace so that the browser back button works + onSelectSchema: (databaseId, schemaId, { useReplace = false } = {}) => { + dispatch( + useReplace + ? replace(Urls.dataModelSchema(databaseId, schemaId)) + : push(Urls.dataModelSchema(databaseId, schemaId)), + ); + }, }); const MetadataSchemaList = ({ @@ -58,7 +68,9 @@ const MetadataSchemaList = ({ useLayoutEffect(() => { if (allSchemas.length === 1 && selectedSchemaId == null) { - onSelectSchema(selectedDatabaseId, allSchemas[0].id); + onSelectSchema(selectedDatabaseId, allSchemas[0].id, { + useReplace: true, + }); } }, [selectedDatabaseId, selectedSchemaId, allSchemas, onSelectSchema]);