From 3051967c9474a1fdb3896c1778305fc91db34da6 Mon Sep 17 00:00:00 2001
From: Alexander Polyankin <alexander.polyankin@metabase.com>
Date: Thu, 1 Jun 2023 20:34:09 +0300
Subject: [PATCH] Migrate home to redux toolkit and unify jest tests (#31213)

---
 frontend/src/metabase-types/api/user.ts       |   4 +-
 .../use-entity-list-query.ts                  |  10 +-
 .../CustomHomePageModal.tsx                   |  14 +-
 .../components/CustomHomePageModal/index.ts   |   1 +
 .../components/HomeCaption/HomeCaption.tsx    |  10 +-
 .../HomeCaption/HomeCaption.unit.spec.tsx     |  11 +-
 .../home/components/HomeCaption/index.ts      |   3 +-
 .../home/components/HomeCard/HomeCard.tsx     |   7 +-
 .../HomeCard/HomeCard.unit.spec.tsx           |  11 +-
 .../home/components/HomeCard/index.ts         |   3 +-
 .../components/HomeContent/HomeContent.tsx    |  77 +++++-----
 .../HomeContent/HomeContent.unit.spec.tsx     | 136 ++++++++----------
 .../home/components/HomeContent/index.ts      |   3 +-
 .../components/HomeGreeting/HomeGreeting.tsx  |  25 +---
 .../HomeGreeting/HomeGreeting.unit.spec.tsx   |   2 +-
 .../home/components/HomeGreeting/index.ts     |   3 +-
 .../components/HomeHelpCard/HomeHelpCard.tsx  |   5 +-
 .../HomeHelpCard/HomeHelpCard.unit.spec.tsx   |  11 +-
 .../home/components/HomeHelpCard/index.ts     |   3 +-
 .../home/components/HomeLayout/HomeLayout.tsx |  34 ++---
 .../HomeLayout/HomeLayout.unit.spec.tsx       |   8 +-
 .../home/components/HomeLayout/index.ts       |   3 +-
 .../HomeModelCard/HomeModelCard.tsx           |   9 +-
 .../HomeModelCard/HomeModelCard.unit.spec.tsx |  26 ++--
 .../home/components/HomeModelCard/index.ts    |   3 +-
 .../home/components/HomePage/HomePage.tsx     |  71 ++++++---
 .../HomePage/HomePage.unit.spec.tsx           |  89 +++++++-----
 .../home/components/HomePage/index.ts         |   3 +-
 .../HomePopularSection/HomePopularSection.tsx |  32 ++---
 .../HomePopularSection.unit.spec.tsx          |  77 +++++-----
 .../components/HomePopularSection/index.ts    |   3 +-
 .../HomeRecentSection/HomeRecentSection.tsx   |  46 ++----
 .../HomeRecentSection.unit.spec.tsx           |  40 +++---
 .../components/HomeRecentSection/index.ts     |   3 +-
 .../components/HomeXrayCard/HomeXrayCard.tsx  |   9 +-
 .../HomeXrayCard/HomeXrayCard.unit.spec.tsx   |  26 ++--
 .../home/components/HomeXrayCard/index.ts     |   3 +-
 .../HomeXraySection/HomeXraySection.tsx       |  53 +++++--
 .../HomeXraySection.unit.spec.tsx             |  52 ++++---
 .../home/components/HomeXraySection/index.ts  |   3 +-
 .../containers/HomeContent/HomeContent.tsx    |  22 ---
 .../home/containers/HomeContent/index.ts      |   2 -
 .../containers/HomePageApp/HomePageApp.tsx    |  53 -------
 .../HomePageApp/HomePageApp.unit.spec.tsx     |  54 -------
 .../home/containers/HomePageApp/index.ts      |   2 -
 .../HomeXraySection/HomeXraySection.tsx       |  34 -----
 .../home/containers/HomeXraySection/index.ts  |   2 -
 frontend/src/metabase/home/selectors.ts       |  25 ++++
 frontend/src/metabase/routes.jsx              |   4 +-
 frontend/src/metabase/selectors/user.ts       |   5 -
 50 files changed, 505 insertions(+), 630 deletions(-)
 rename frontend/src/metabase/home/components/{Modals => }/CustomHomePageModal/CustomHomePageModal.tsx (87%)
 create mode 100644 frontend/src/metabase/home/components/CustomHomePageModal/index.ts
 delete mode 100644 frontend/src/metabase/home/containers/HomeContent/HomeContent.tsx
 delete mode 100644 frontend/src/metabase/home/containers/HomeContent/index.ts
 delete mode 100644 frontend/src/metabase/home/containers/HomePageApp/HomePageApp.tsx
 delete mode 100644 frontend/src/metabase/home/containers/HomePageApp/HomePageApp.unit.spec.tsx
 delete mode 100644 frontend/src/metabase/home/containers/HomePageApp/index.ts
 delete mode 100644 frontend/src/metabase/home/containers/HomeXraySection/HomeXraySection.tsx
 delete mode 100644 frontend/src/metabase/home/containers/HomeXraySection/index.ts
 create mode 100644 frontend/src/metabase/home/selectors.ts

diff --git a/frontend/src/metabase-types/api/user.ts b/frontend/src/metabase-types/api/user.ts
index 70cefe2ae84..a5e2973c1e6 100644
--- a/frontend/src/metabase-types/api/user.ts
+++ b/frontend/src/metabase-types/api/user.ts
@@ -1,3 +1,5 @@
+import { DashboardId } from "./dashboard";
+
 export type UserId = number;
 
 export type UserAttribute = string;
@@ -27,7 +29,7 @@ export interface User extends BaseUser {
   has_question_and_dashboard: boolean;
   personal_collection_id: number;
   custom_homepage: {
-    dashboard_id: number;
+    dashboard_id: DashboardId;
   } | null;
 }
 
diff --git a/frontend/src/metabase/common/hooks/use-entity-list-query/use-entity-list-query.ts b/frontend/src/metabase/common/hooks/use-entity-list-query/use-entity-list-query.ts
index 6bd034014cf..f2a46d18c5e 100644
--- a/frontend/src/metabase/common/hooks/use-entity-list-query/use-entity-list-query.ts
+++ b/frontend/src/metabase/common/hooks/use-entity-list-query/use-entity-list-query.ts
@@ -1,4 +1,4 @@
-import { useDeepCompareEffect } from "react-use";
+import { useDeepCompareEffect, usePrevious } from "react-use";
 import type { Action } from "@reduxjs/toolkit";
 import { useDispatch, useSelector } from "metabase/lib/redux";
 import { State } from "metabase-types/store";
@@ -50,17 +50,19 @@ export const useEntityListQuery = <TItem, TQuery = never>(
 ): UseEntityListQueryResult<TItem> => {
   const options = { entityQuery };
   const data = useSelector(state => getList(state, options));
+  const error = useSelector(state => getError(state, options));
   const isLoading = useSelector(state => getLoading(state, options));
   const isLoaded = useSelector(state => getLoaded(state, options));
-  const error = useSelector(state => getError(state, options));
+  const isLoadedPreviously = usePrevious(isLoaded);
+  const isInvalidated = !isLoaded && isLoadedPreviously;
 
   const dispatch = useDispatch();
   useDeepCompareEffect(() => {
-    if (enabled && !isLoaded) {
+    if (enabled || (enabled && isInvalidated)) {
       const action = dispatch(fetchList(entityQuery, { reload }));
       Promise.resolve(action).catch(() => undefined);
     }
-  }, [dispatch, fetchList, entityQuery, reload, enabled, isLoaded]);
+  }, [dispatch, fetchList, entityQuery, reload, enabled, isInvalidated]);
 
   return { data, isLoading, error };
 };
diff --git a/frontend/src/metabase/home/components/Modals/CustomHomePageModal/CustomHomePageModal.tsx b/frontend/src/metabase/home/components/CustomHomePageModal/CustomHomePageModal.tsx
similarity index 87%
rename from frontend/src/metabase/home/components/Modals/CustomHomePageModal/CustomHomePageModal.tsx
rename to frontend/src/metabase/home/components/CustomHomePageModal/CustomHomePageModal.tsx
index 4a6c415495b..a9481531de4 100644
--- a/frontend/src/metabase/home/components/Modals/CustomHomePageModal/CustomHomePageModal.tsx
+++ b/frontend/src/metabase/home/components/CustomHomePageModal/CustomHomePageModal.tsx
@@ -23,13 +23,13 @@ export const CustomHomePageModal = ({
   isOpen,
   onClose,
 }: CustomHomePageModalProps) => {
-  const [dashboard, setDashboard] = useState<DashboardId>();
+  const [dashboardId, setDashboardId] = useState<DashboardId>();
   const dispatch = useDispatch();
 
   const handleSave = async () => {
     await dispatch(
       updateSettings({
-        [CUSTOM_HOMEPAGE_DASHBOARD_SETTING_KEY]: dashboard,
+        [CUSTOM_HOMEPAGE_DASHBOARD_SETTING_KEY]: dashboardId,
         [CUSTOM_HOMEPAGE_SETTING_KEY]: true,
       }),
     );
@@ -37,14 +37,14 @@ export const CustomHomePageModal = ({
   };
 
   const handleChange = useCallback(
-    (value: number | null | undefined | string) => {
+    (value: DashboardId | null | undefined) => {
       if (value) {
-        setDashboard(value);
+        setDashboardId(value);
       } else {
-        setDashboard(undefined);
+        setDashboardId(undefined);
       }
     },
-    [setDashboard],
+    [setDashboardId],
   );
 
   return (
@@ -63,7 +63,7 @@ export const CustomHomePageModal = ({
       >
         <p>{t`Pick one of your dashboards to serve as homepage. Users without dashboard access will be directed to the default homepage. You can update or reset this anytime in Admin Settings > Settings > General`}</p>
         <DashboardSelector
-          value={dashboard}
+          value={dashboardId}
           onChange={handleChange}
           collectionFilter={(collection: Collection) =>
             collection.personal_owner_id === null || collection.id === "root"
diff --git a/frontend/src/metabase/home/components/CustomHomePageModal/index.ts b/frontend/src/metabase/home/components/CustomHomePageModal/index.ts
new file mode 100644
index 00000000000..c79c8c4b31c
--- /dev/null
+++ b/frontend/src/metabase/home/components/CustomHomePageModal/index.ts
@@ -0,0 +1 @@
+export * from "./CustomHomePageModal";
diff --git a/frontend/src/metabase/home/components/HomeCaption/HomeCaption.tsx b/frontend/src/metabase/home/components/HomeCaption/HomeCaption.tsx
index f4397725832..169add72026 100644
--- a/frontend/src/metabase/home/components/HomeCaption/HomeCaption.tsx
+++ b/frontend/src/metabase/home/components/HomeCaption/HomeCaption.tsx
@@ -1,14 +1,14 @@
 import { ReactNode } from "react";
 import { CaptionRoot } from "./HomeCaption.styled";
 
-export interface HomeCaptionProps {
+interface HomeCaptionProps {
   primary?: boolean;
   children?: ReactNode;
 }
 
-const HomeCaption = ({ primary, children }: HomeCaptionProps): JSX.Element => {
+export const HomeCaption = ({
+  primary,
+  children,
+}: HomeCaptionProps): JSX.Element => {
   return <CaptionRoot primary={primary}>{children}</CaptionRoot>;
 };
-
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export default HomeCaption;
diff --git a/frontend/src/metabase/home/components/HomeCaption/HomeCaption.unit.spec.tsx b/frontend/src/metabase/home/components/HomeCaption/HomeCaption.unit.spec.tsx
index 5641bb406f2..f4ca106f699 100644
--- a/frontend/src/metabase/home/components/HomeCaption/HomeCaption.unit.spec.tsx
+++ b/frontend/src/metabase/home/components/HomeCaption/HomeCaption.unit.spec.tsx
@@ -1,10 +1,13 @@
-import { render, screen } from "@testing-library/react";
-import HomeCaption from "./HomeCaption";
+import { render, screen } from "__support__/ui";
+import { HomeCaption } from "./HomeCaption";
+
+const setup = () => {
+  render(<HomeCaption>Title</HomeCaption>);
+};
 
 describe("HomeCaption", () => {
   it("should render correctly", () => {
-    render(<HomeCaption>Title</HomeCaption>);
-
+    setup();
     expect(screen.getByText("Title")).toBeInTheDocument();
   });
 });
diff --git a/frontend/src/metabase/home/components/HomeCaption/index.ts b/frontend/src/metabase/home/components/HomeCaption/index.ts
index eab17793d4f..efc7c457b24 100644
--- a/frontend/src/metabase/home/components/HomeCaption/index.ts
+++ b/frontend/src/metabase/home/components/HomeCaption/index.ts
@@ -1,2 +1 @@
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export { default } from "./HomeCaption";
+export * from "./HomeCaption";
diff --git a/frontend/src/metabase/home/components/HomeCard/HomeCard.tsx b/frontend/src/metabase/home/components/HomeCard/HomeCard.tsx
index b4e7f17f087..781828d5cf0 100644
--- a/frontend/src/metabase/home/components/HomeCard/HomeCard.tsx
+++ b/frontend/src/metabase/home/components/HomeCard/HomeCard.tsx
@@ -1,14 +1,14 @@
 import { ReactNode } from "react";
 import { CardRoot } from "./HomeCard.styled";
 
-export interface HomeCardProps {
+interface HomeCardProps {
   className?: string;
   url?: string;
   external?: boolean;
   children?: ReactNode;
 }
 
-const HomeCard = ({
+export const HomeCard = ({
   className,
   url = "",
   children,
@@ -19,6 +19,3 @@ const HomeCard = ({
     </CardRoot>
   );
 };
-
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export default HomeCard;
diff --git a/frontend/src/metabase/home/components/HomeCard/HomeCard.unit.spec.tsx b/frontend/src/metabase/home/components/HomeCard/HomeCard.unit.spec.tsx
index e12df0cee83..753b8a38739 100644
--- a/frontend/src/metabase/home/components/HomeCard/HomeCard.unit.spec.tsx
+++ b/frontend/src/metabase/home/components/HomeCard/HomeCard.unit.spec.tsx
@@ -1,10 +1,13 @@
-import { render, screen } from "@testing-library/react";
-import HomeCard from "./HomeCard";
+import { render, screen } from "__support__/ui";
+import { HomeCard } from "./HomeCard";
+
+const setup = () => {
+  render(<HomeCard>A look at table</HomeCard>);
+};
 
 describe("HomeCard", () => {
   it("should render correctly", () => {
-    render(<HomeCard>A look at table</HomeCard>);
-
+    setup();
     expect(screen.getByText("A look at table")).toBeInTheDocument();
   });
 });
diff --git a/frontend/src/metabase/home/components/HomeCard/index.ts b/frontend/src/metabase/home/components/HomeCard/index.ts
index 9a209044c5a..46b2bea9a12 100644
--- a/frontend/src/metabase/home/components/HomeCard/index.ts
+++ b/frontend/src/metabase/home/components/HomeCard/index.ts
@@ -1,2 +1 @@
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export { default } from "./HomeCard";
+export * from "./HomeCard";
diff --git a/frontend/src/metabase/home/components/HomeContent/HomeContent.tsx b/frontend/src/metabase/home/components/HomeContent/HomeContent.tsx
index 139265e72cc..6ba8790e239 100644
--- a/frontend/src/metabase/home/components/HomeContent/HomeContent.tsx
+++ b/frontend/src/metabase/home/components/HomeContent/HomeContent.tsx
@@ -1,46 +1,52 @@
+import { useSelector } from "metabase/lib/redux";
 import { isSyncCompleted } from "metabase/lib/syncing";
+import { getUser } from "metabase/selectors/user";
 import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper";
+import {
+  useDatabaseListQuery,
+  usePopularItemListQuery,
+  useRecentItemListQuery,
+} from "metabase/common/hooks";
 import { PopularItem, RecentItem, User } from "metabase-types/api";
 import Database from "metabase-lib/metadata/Database";
-import HomePopularSection from "../HomePopularSection";
-import HomeRecentSection from "../HomeRecentSection";
-import HomeXraySection from "../../containers/HomeXraySection";
+import { HomePopularSection } from "../HomePopularSection";
+import { HomeRecentSection } from "../HomeRecentSection";
+import { HomeXraySection } from "../HomeXraySection";
+import { getIsXrayEnabled } from "../../selectors";
 import { isWithinWeeks } from "../../utils";
 
-export interface HomeContentProps {
-  user: User;
-  databases?: Database[];
-  recentItems?: RecentItem[];
-  popularItems?: PopularItem[];
-  isXrayEnabled: boolean;
-}
+export const HomeContent = (): JSX.Element | null => {
+  const user = useSelector(getUser);
+  const isXrayEnabled = useSelector(getIsXrayEnabled);
+  const { data: databases } = useDatabaseListQuery();
+  const { data: recentItems } = useRecentItemListQuery({ reload: true });
+  const { data: popularItems } = usePopularItemListQuery({ reload: true });
 
-const HomeContent = (props: HomeContentProps): JSX.Element | null => {
-  if (isLoading(props)) {
+  if (!user || isLoading(user, databases, recentItems, popularItems)) {
     return <LoadingAndErrorWrapper loading />;
   }
 
-  if (isPopularSection(props)) {
+  if (isPopularSection(user, recentItems, popularItems)) {
     return <HomePopularSection />;
   }
 
-  if (isRecentSection(props)) {
+  if (isRecentSection(user, recentItems)) {
     return <HomeRecentSection />;
   }
 
-  if (isXraySection(props)) {
+  if (isXraySection(databases, isXrayEnabled)) {
     return <HomeXraySection />;
   }
 
   return null;
 };
 
-const isLoading = ({
-  user,
-  databases,
-  recentItems,
-  popularItems,
-}: HomeContentProps): boolean => {
+const isLoading = (
+  user: User,
+  databases: Database[] | undefined,
+  recentItems: RecentItem[] | undefined,
+  popularItems: PopularItem[] | undefined,
+): boolean => {
   if (!user.has_question_and_dashboard) {
     return databases == null;
   } else if (user.is_installer || !isWithinWeeks(user.first_login, 1)) {
@@ -50,11 +56,11 @@ const isLoading = ({
   }
 };
 
-const isPopularSection = ({
-  user,
-  recentItems = [],
-  popularItems = [],
-}: HomeContentProps): boolean => {
+const isPopularSection = (
+  user: User,
+  recentItems: RecentItem[] = [],
+  popularItems: PopularItem[] = [],
+): boolean => {
   return (
     !user.is_installer &&
     user.has_question_and_dashboard &&
@@ -63,19 +69,16 @@ const isPopularSection = ({
   );
 };
 
-const isRecentSection = ({
-  user,
-  recentItems = [],
-}: HomeContentProps): boolean => {
+const isRecentSection = (
+  user: User,
+  recentItems: RecentItem[] = [],
+): boolean => {
   return user.has_question_and_dashboard && recentItems.length > 0;
 };
 
-const isXraySection = ({
-  databases = [],
-  isXrayEnabled,
-}: HomeContentProps): boolean => {
+const isXraySection = (
+  databases: Database[] = [],
+  isXrayEnabled: boolean,
+): boolean => {
   return databases.some(isSyncCompleted) && isXrayEnabled;
 };
-
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export default HomeContent;
diff --git a/frontend/src/metabase/home/components/HomeContent/HomeContent.unit.spec.tsx b/frontend/src/metabase/home/components/HomeContent/HomeContent.unit.spec.tsx
index 981d046bc0c..6af29871397 100644
--- a/frontend/src/metabase/home/components/HomeContent/HomeContent.unit.spec.tsx
+++ b/frontend/src/metabase/home/components/HomeContent/HomeContent.unit.spec.tsx
@@ -1,5 +1,3 @@
-import { checkNotNull } from "metabase/core/utils/types";
-import { getMetadata } from "metabase/selectors/metadata";
 import { Database, PopularItem, RecentItem, User } from "metabase-types/api";
 import {
   createMockDatabase,
@@ -7,19 +5,22 @@ import {
   createMockRecentItem,
   createMockUser,
 } from "metabase-types/api/mocks";
-import { createMockState } from "metabase-types/store/mocks";
-import { createMockEntitiesState } from "__support__/store";
-import { renderWithProviders, screen } from "__support__/ui";
-import HomeContent from "./HomeContent";
-
-const PopularSectionMock = () => <div>PopularSection</div>;
-jest.mock("../HomePopularSection", () => PopularSectionMock);
-
-const RecentSectionMock = () => <div>RecentSection</div>;
-jest.mock("../HomeRecentSection", () => RecentSectionMock);
-
-const XraySectionMock = () => <div>XraySection</div>;
-jest.mock("../../containers/HomeXraySection", () => XraySectionMock);
+import {
+  createMockSettingsState,
+  createMockState,
+} from "metabase-types/store/mocks";
+import {
+  renderWithProviders,
+  screen,
+  waitForElementToBeRemoved,
+} from "__support__/ui";
+import {
+  setupDatabaseCandidatesEndpoint,
+  setupDatabasesEndpoints,
+  setupPopularItemsEndpoints,
+  setupRecentViewsEndpoints,
+} from "__support__/server-mocks";
+import { HomeContent } from "./HomeContent";
 
 interface SetupOpts {
   user: User;
@@ -29,35 +30,33 @@ interface SetupOpts {
   isXrayEnabled?: boolean;
 }
 
-const setup = ({
+const setup = async ({
   user,
-  databases,
-  recentItems,
-  popularItems,
+  databases = [],
+  recentItems = [],
+  popularItems = [],
   isXrayEnabled = true,
 }: SetupOpts) => {
   const state = createMockState({
-    entities: createMockEntitiesState({ databases }),
+    currentUser: user,
+    settings: createMockSettingsState({
+      "enable-xrays": isXrayEnabled,
+    }),
   });
-  const metadata = getMetadata(state);
-
-  renderWithProviders(
-    <HomeContent
-      user={user}
-      databases={databases?.map(({ id }) =>
-        checkNotNull(metadata.database(id)),
-      )}
-      recentItems={recentItems}
-      popularItems={popularItems}
-      isXrayEnabled={isXrayEnabled}
-    />,
-    { storeInitialState: state },
-  );
+
+  setupDatabasesEndpoints(databases);
+  setupRecentViewsEndpoints(recentItems);
+  setupPopularItemsEndpoints(popularItems);
+  databases.forEach(({ id }) => setupDatabaseCandidatesEndpoint(id, []));
+
+  renderWithProviders(<HomeContent />, { storeInitialState: state });
+
+  await waitForElementToBeRemoved(() => screen.queryByText(/Loading/i));
 };
 
 describe("HomeContent", () => {
   beforeEach(() => {
-    jest.useFakeTimers();
+    jest.useFakeTimers({ advanceTimers: true });
     jest.setSystemTime(new Date(2020, 0, 10));
   });
 
@@ -65,8 +64,8 @@ describe("HomeContent", () => {
     jest.useRealTimers();
   });
 
-  it("should render popular items for a new user", () => {
-    setup({
+  it("should render popular items for a new user", async () => {
+    await setup({
       user: createMockUser({
         is_installer: false,
         has_question_and_dashboard: true,
@@ -77,26 +76,29 @@ describe("HomeContent", () => {
       popularItems: [createMockPopularItem()],
     });
 
-    expect(screen.getByText("PopularSection")).toBeInTheDocument();
+    expect(
+      screen.getByText("Here are some popular tables"),
+    ).toBeInTheDocument();
   });
 
-  it("should render popular items for a user without recent items", () => {
-    setup({
+  it("should render popular items for a user without recent items", async () => {
+    await setup({
       user: createMockUser({
         is_installer: false,
         has_question_and_dashboard: true,
         first_login: "2020-01-05T00:00:00Z",
       }),
       databases: [createMockDatabase()],
-      recentItems: [],
       popularItems: [createMockPopularItem()],
     });
 
-    expect(screen.getByText("PopularSection")).toBeInTheDocument();
+    expect(
+      screen.getByText("Here are some popular tables"),
+    ).toBeInTheDocument();
   });
 
-  it("should render recent items for an existing user", () => {
-    setup({
+  it("should render recent items for an existing user", async () => {
+    await setup({
       user: createMockUser({
         is_installer: false,
         has_question_and_dashboard: true,
@@ -106,25 +108,24 @@ describe("HomeContent", () => {
       recentItems: [createMockRecentItem()],
     });
 
-    expect(screen.getByText("RecentSection")).toBeInTheDocument();
+    expect(screen.getByText("Pick up where you left off")).toBeInTheDocument();
   });
 
-  it("should render x-rays for an installer after the setup", () => {
-    setup({
+  it("should render x-rays for an installer after the setup", async () => {
+    await setup({
       user: createMockUser({
         is_installer: true,
         has_question_and_dashboard: false,
         first_login: "2020-01-10T00:00:00Z",
       }),
       databases: [createMockDatabase()],
-      recentItems: [],
     });
 
-    expect(screen.getByText("XraySection")).toBeInTheDocument();
+    expect(screen.getByText(/Here are some explorations/)).toBeInTheDocument();
   });
 
-  it("should render x-rays for the installer when there is no question and dashboard", () => {
-    setup({
+  it("should render x-rays for the installer when there is no question and dashboard", async () => {
+    await setup({
       user: createMockUser({
         is_installer: true,
         has_question_and_dashboard: false,
@@ -134,11 +135,11 @@ describe("HomeContent", () => {
       recentItems: [createMockRecentItem()],
     });
 
-    expect(screen.getByText("XraySection")).toBeInTheDocument();
+    expect(screen.getByText(/Here are some explorations/)).toBeInTheDocument();
   });
 
-  it("should not render x-rays for the installer when there is no question and dashboard if the x-rays feature is disabled", () => {
-    setup({
+  it("should not render x-rays for the installer when there is no question and dashboard if the x-rays feature is disabled", async () => {
+    await setup({
       user: createMockUser({
         is_installer: true,
         has_question_and_dashboard: false,
@@ -149,33 +150,22 @@ describe("HomeContent", () => {
       isXrayEnabled: false,
     });
 
-    expect(screen.queryByText("XraySection")).not.toBeInTheDocument();
-  });
-
-  it("should render nothing if there are no databases", () => {
-    setup({
-      user: createMockUser({
-        is_installer: true,
-        has_question_and_dashboard: false,
-        first_login: "2020-01-10T00:00:00Z",
-      }),
-      databases: [],
-      recentItems: [],
-    });
-
-    expect(screen.queryByText("XraySection")).not.toBeInTheDocument();
+    expect(
+      screen.queryByText(/Here are some explorations/),
+    ).not.toBeInTheDocument();
   });
 
-  it("should render loading state if there is not enough data to choose a section", () => {
-    setup({
+  it("should render nothing if there are no databases", async () => {
+    await setup({
       user: createMockUser({
         is_installer: true,
         has_question_and_dashboard: false,
         first_login: "2020-01-10T00:00:00Z",
       }),
-      databases: undefined,
     });
 
-    expect(screen.getByText("Loading...")).toBeInTheDocument();
+    expect(
+      screen.queryByText(/Here are some explorations/),
+    ).not.toBeInTheDocument();
   });
 });
diff --git a/frontend/src/metabase/home/components/HomeContent/index.ts b/frontend/src/metabase/home/components/HomeContent/index.ts
index acc76cd9ddd..37350dbc698 100644
--- a/frontend/src/metabase/home/components/HomeContent/index.ts
+++ b/frontend/src/metabase/home/components/HomeContent/index.ts
@@ -1,2 +1 @@
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export { default } from "./HomeContent";
+export * from "./HomeContent";
diff --git a/frontend/src/metabase/home/components/HomeGreeting/HomeGreeting.tsx b/frontend/src/metabase/home/components/HomeGreeting/HomeGreeting.tsx
index c381008484c..a341905228b 100644
--- a/frontend/src/metabase/home/components/HomeGreeting/HomeGreeting.tsx
+++ b/frontend/src/metabase/home/components/HomeGreeting/HomeGreeting.tsx
@@ -1,31 +1,19 @@
 import { useMemo } from "react";
-import { connect } from "react-redux";
 import { t } from "ttag";
 import _ from "underscore";
+import { useSelector } from "metabase/lib/redux";
 import Tooltip from "metabase/core/components/Tooltip";
 import { getUser } from "metabase/selectors/user";
-import { getSetting } from "metabase/selectors/settings";
-import { User } from "metabase-types/api";
-import { State } from "metabase-types/store";
+import { getHasMetabotLogo } from "../../selectors";
 import {
   GreetingLogo,
   GreetingMessage,
   GreetingRoot,
 } from "./HomeGreeting.styled";
 
-interface StateProps {
-  user: User | null;
-  showLogo?: boolean;
-}
-
-type HomeGreetingProps = StateProps;
-
-const mapStateToProps = (state: State): StateProps => ({
-  user: getUser(state),
-  showLogo: getSetting(state, "show-metabot"),
-});
-
-const HomeGreeting = ({ user, showLogo }: HomeGreetingProps): JSX.Element => {
+export const HomeGreeting = (): JSX.Element => {
+  const user = useSelector(getUser);
+  const showLogo = useSelector(getHasMetabotLogo);
   const name = user?.first_name;
   const message = useMemo(() => getMessage(name), [name]);
 
@@ -56,6 +44,3 @@ const getMessage = (name: string | null | undefined): string => {
 
   return _.sample(options) ?? "";
 };
-
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export default connect(mapStateToProps)(HomeGreeting);
diff --git a/frontend/src/metabase/home/components/HomeGreeting/HomeGreeting.unit.spec.tsx b/frontend/src/metabase/home/components/HomeGreeting/HomeGreeting.unit.spec.tsx
index 1295fd31a9e..0aae6c7ec7c 100644
--- a/frontend/src/metabase/home/components/HomeGreeting/HomeGreeting.unit.spec.tsx
+++ b/frontend/src/metabase/home/components/HomeGreeting/HomeGreeting.unit.spec.tsx
@@ -5,7 +5,7 @@ import {
   createMockSettingsState,
   createMockState,
 } from "metabase-types/store/mocks";
-import HomeGreeting from "./HomeGreeting";
+import { HomeGreeting } from "./HomeGreeting";
 
 interface SetupOpts {
   currentUser?: User;
diff --git a/frontend/src/metabase/home/components/HomeGreeting/index.ts b/frontend/src/metabase/home/components/HomeGreeting/index.ts
index f58e73bb891..f9e9c333a00 100644
--- a/frontend/src/metabase/home/components/HomeGreeting/index.ts
+++ b/frontend/src/metabase/home/components/HomeGreeting/index.ts
@@ -1,2 +1 @@
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export { default } from "./HomeGreeting";
+export * from "./HomeGreeting";
diff --git a/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.tsx b/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.tsx
index 673730ed6d5..eda1ddb0fdb 100644
--- a/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.tsx
+++ b/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.tsx
@@ -2,7 +2,7 @@ import { t } from "ttag";
 import MetabaseSettings from "metabase/lib/settings";
 import { CardIcon, CardRoot, CardTitle } from "./HomeHelpCard.styled";
 
-const HomeHelpCard = (): JSX.Element => {
+export const HomeHelpCard = (): JSX.Element => {
   return (
     <CardRoot href={MetabaseSettings.learnUrl()}>
       <CardIcon name="reference" />
@@ -10,6 +10,3 @@ const HomeHelpCard = (): JSX.Element => {
     </CardRoot>
   );
 };
-
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export default HomeHelpCard;
diff --git a/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.unit.spec.tsx b/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.unit.spec.tsx
index 9e6ca6ca150..e5a7aa71793 100644
--- a/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.unit.spec.tsx
+++ b/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.unit.spec.tsx
@@ -1,10 +1,13 @@
-import { render, screen } from "@testing-library/react";
-import HomeHelpCard from "./HomeHelpCard";
+import { render, screen } from "__support__/ui";
+import { HomeHelpCard } from "./HomeHelpCard";
+
+const setup = () => {
+  render(<HomeHelpCard />);
+};
 
 describe("HomeHelpCard", () => {
   it("should render correctly", () => {
-    render(<HomeHelpCard />);
-
+    setup();
     expect(screen.getByText("Metabase tips")).toBeInTheDocument();
   });
 });
diff --git a/frontend/src/metabase/home/components/HomeHelpCard/index.ts b/frontend/src/metabase/home/components/HomeHelpCard/index.ts
index d2e4fcca41f..4ba1e46a4c4 100644
--- a/frontend/src/metabase/home/components/HomeHelpCard/index.ts
+++ b/frontend/src/metabase/home/components/HomeHelpCard/index.ts
@@ -1,2 +1 @@
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export { default } from "./HomeHelpCard";
+export * from "./HomeHelpCard";
diff --git a/frontend/src/metabase/home/components/HomeLayout/HomeLayout.tsx b/frontend/src/metabase/home/components/HomeLayout/HomeLayout.tsx
index 31314e8a75e..be618a6530e 100644
--- a/frontend/src/metabase/home/components/HomeLayout/HomeLayout.tsx
+++ b/frontend/src/metabase/home/components/HomeLayout/HomeLayout.tsx
@@ -1,12 +1,11 @@
 import { ReactNode, useState } from "react";
-import { connect } from "react-redux";
-import { getSetting } from "metabase/selectors/settings";
+import { useSelector } from "metabase/lib/redux";
 import { getUserIsAdmin } from "metabase/selectors/user";
 import MetabotWidget from "metabase/metabot/components/MetabotWidget";
-import { State } from "metabase-types/store";
 import Tooltip from "metabase/core/components/Tooltip/Tooltip";
-import HomeGreeting from "../HomeGreeting";
-import { CustomHomePageModal } from "../Modals/CustomHomePageModal/CustomHomePageModal";
+import { HomeGreeting } from "../HomeGreeting";
+import { getHasIllustration } from "../../selectors";
+import { CustomHomePageModal } from "../CustomHomePageModal";
 import {
   LayoutBody,
   LayoutEditButton,
@@ -14,30 +13,18 @@ import {
   LayoutRoot,
 } from "./HomeLayout.styled";
 
-interface OwnProps {
-  hasMetabot?: boolean;
+interface HomeLayoutProps {
+  hasMetabot: boolean;
   children?: ReactNode;
 }
 
-interface StateProps {
-  hasIllustration?: boolean;
-  isAdmin?: boolean;
-}
-
-type HomeLayoutProps = OwnProps & StateProps;
-
-const mapStateToProps = (state: State) => ({
-  hasIllustration: getSetting(state, "show-lighthouse-illustration"),
-  isAdmin: getUserIsAdmin(state),
-});
-
-const HomeLayout = ({
+export const HomeLayout = ({
   hasMetabot,
-  hasIllustration,
   children,
-  isAdmin,
 }: HomeLayoutProps): JSX.Element => {
   const [showModal, setShowModal] = useState(false);
+  const isAdmin = useSelector(getUserIsAdmin);
+  const hasIllustration = useSelector(getHasIllustration);
 
   return (
     <LayoutRoot>
@@ -62,6 +49,3 @@ const HomeLayout = ({
     </LayoutRoot>
   );
 };
-
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export default connect(mapStateToProps)(HomeLayout);
diff --git a/frontend/src/metabase/home/components/HomeLayout/HomeLayout.unit.spec.tsx b/frontend/src/metabase/home/components/HomeLayout/HomeLayout.unit.spec.tsx
index a1801a96715..bf69ab287f9 100644
--- a/frontend/src/metabase/home/components/HomeLayout/HomeLayout.unit.spec.tsx
+++ b/frontend/src/metabase/home/components/HomeLayout/HomeLayout.unit.spec.tsx
@@ -1,8 +1,8 @@
-import { renderWithProviders, screen } from "__support__/ui";
 import { User } from "metabase-types/api";
+import { renderWithProviders, screen } from "__support__/ui";
 import { createMockUser } from "metabase-types/api/mocks";
 import { createMockState } from "metabase-types/store/mocks";
-import HomeLayout from "./HomeLayout";
+import { HomeLayout } from "./HomeLayout";
 
 interface SetupOpts {
   currentUser?: User;
@@ -13,7 +13,9 @@ const setup = ({ currentUser }: SetupOpts = {}) => {
     currentUser,
   });
 
-  renderWithProviders(<HomeLayout />, { storeInitialState: state });
+  renderWithProviders(<HomeLayout hasMetabot={false} />, {
+    storeInitialState: state,
+  });
 };
 
 describe("HomeLayout", () => {
diff --git a/frontend/src/metabase/home/components/HomeLayout/index.ts b/frontend/src/metabase/home/components/HomeLayout/index.ts
index 5aa9e4a4bdd..f498bac3c0c 100644
--- a/frontend/src/metabase/home/components/HomeLayout/index.ts
+++ b/frontend/src/metabase/home/components/HomeLayout/index.ts
@@ -1,2 +1 @@
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export { default } from "./HomeLayout";
+export * from "./HomeLayout";
diff --git a/frontend/src/metabase/home/components/HomeModelCard/HomeModelCard.tsx b/frontend/src/metabase/home/components/HomeModelCard/HomeModelCard.tsx
index a002aa1c0aa..890d062d4a4 100644
--- a/frontend/src/metabase/home/components/HomeModelCard/HomeModelCard.tsx
+++ b/frontend/src/metabase/home/components/HomeModelCard/HomeModelCard.tsx
@@ -1,7 +1,7 @@
-import HomeCard from "../HomeCard";
+import { HomeCard } from "../HomeCard";
 import { CardIcon, CardTitle } from "./HomeModelCard.styled";
 
-export interface HomeModelCardProps {
+interface HomeModelCardProps {
   title: string;
   icon: HomeModelIconProps;
   url: string;
@@ -11,7 +11,7 @@ export interface HomeModelIconProps {
   name: string;
 }
 
-const HomeModelCard = ({
+export const HomeModelCard = ({
   title,
   icon,
   url,
@@ -23,6 +23,3 @@ const HomeModelCard = ({
     </HomeCard>
   );
 };
-
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export default HomeModelCard;
diff --git a/frontend/src/metabase/home/components/HomeModelCard/HomeModelCard.unit.spec.tsx b/frontend/src/metabase/home/components/HomeModelCard/HomeModelCard.unit.spec.tsx
index 90b29220f34..ef90340b8c8 100644
--- a/frontend/src/metabase/home/components/HomeModelCard/HomeModelCard.unit.spec.tsx
+++ b/frontend/src/metabase/home/components/HomeModelCard/HomeModelCard.unit.spec.tsx
@@ -1,23 +1,25 @@
-import { render, screen } from "@testing-library/react";
-import HomeModelCard, { HomeModelCardProps } from "./HomeModelCard";
+import { render, screen } from "__support__/ui";
+import { HomeModelCard, HomeModelIconProps } from "./HomeModelCard";
+
+interface SetupOpts {
+  title: string;
+  icon: HomeModelIconProps;
+  url: string;
+}
+
+const setup = ({ title, icon, url }: SetupOpts) => {
+  render(<HomeModelCard title={title} icon={icon} url={url} />);
+};
 
 describe("HomeModelCard", () => {
   it("should render correctly", () => {
-    const props = getProps({
+    setup({
       title: "Orders",
       icon: { name: "table" },
+      url: "/question/1",
     });
 
-    render(<HomeModelCard {...props} />);
-
     expect(screen.getByText("Orders")).toBeInTheDocument();
     expect(screen.getByLabelText("table icon")).toBeInTheDocument();
   });
 });
-
-const getProps = (opts?: Partial<HomeModelCardProps>): HomeModelCardProps => ({
-  title: "Orders",
-  icon: { name: "card" },
-  url: "/question/1",
-  ...opts,
-});
diff --git a/frontend/src/metabase/home/components/HomeModelCard/index.ts b/frontend/src/metabase/home/components/HomeModelCard/index.ts
index ba1a983149e..8b703e2ec4f 100644
--- a/frontend/src/metabase/home/components/HomeModelCard/index.ts
+++ b/frontend/src/metabase/home/components/HomeModelCard/index.ts
@@ -1,2 +1 @@
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export { default } from "./HomeModelCard";
+export * from "./HomeModelCard";
diff --git a/frontend/src/metabase/home/components/HomePage/HomePage.tsx b/frontend/src/metabase/home/components/HomePage/HomePage.tsx
index 557c07cd449..1de14f067a8 100644
--- a/frontend/src/metabase/home/components/HomePage/HomePage.tsx
+++ b/frontend/src/metabase/home/components/HomePage/HomePage.tsx
@@ -1,31 +1,55 @@
 import { useEffect } from "react";
 import { replace } from "react-router-redux";
 import { isSmallScreen } from "metabase/lib/dom";
-import { useDispatch } from "metabase/lib/redux";
+import { openNavbar } from "metabase/redux/app";
+import { useDispatch, useSelector } from "metabase/lib/redux";
+import {
+  useDatabaseListQuery,
+  useSearchListQuery,
+} from "metabase/common/hooks";
+import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper";
+import { canUseMetabotOnDatabase } from "metabase/metabot/utils";
+import { CollectionItem } from "metabase-types/api";
+import Database from "metabase-lib/metadata/Database";
+import {
+  getCustomHomePageDashboardId,
+  getIsMetabotEnabled,
+} from "../../selectors";
+import { HomeLayout } from "../HomeLayout";
+import { HomeContent } from "../HomeContent";
 
-import HomeLayout from "../HomeLayout";
-import HomeContent from "../../containers/HomeContent";
+const SEARCH_QUERY = { models: "dataset", limit: 1 } as const;
 
-export interface HomePageProps {
-  hasMetabot: boolean;
-  homepageDashboard?: number;
-  onOpenNavbar: () => void;
-}
-
-const HomePage = ({
-  hasMetabot,
-  onOpenNavbar,
-  homepageDashboard,
-}: HomePageProps): JSX.Element => {
+export const HomePage = (): JSX.Element => {
+  const databaseListState = useDatabaseListQuery();
+  const modelListState = useSearchListQuery({
+    query: SEARCH_QUERY,
+  });
+  const isLoading = databaseListState.isLoading || modelListState.isLoading;
+  const error = databaseListState.error ?? modelListState.error;
+  const dashboardId = useSelector(getCustomHomePageDashboardId);
+  const isMetabotEnabled = useSelector(getIsMetabotEnabled);
+  const hasMetabot = getHasMetabot(
+    databaseListState.data,
+    modelListState.data,
+    isMetabotEnabled,
+  );
   const dispatch = useDispatch();
+
   useEffect(() => {
     if (!isSmallScreen()) {
-      onOpenNavbar();
+      dispatch(openNavbar());
     }
-  }, [onOpenNavbar]);
+  }, [dispatch]);
 
-  if (homepageDashboard) {
-    dispatch(replace(`/dashboard/${homepageDashboard}`));
+  useEffect(() => {
+    if (dashboardId) {
+      dispatch(replace(`/dashboard/${dashboardId}`));
+    }
+  }, [dashboardId, dispatch]);
+
+  if (isLoading || error) {
+    return <LoadingAndErrorWrapper loading={isLoading} error={error} />;
   }
 
   return (
@@ -35,5 +59,12 @@ const HomePage = ({
   );
 };
 
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export default HomePage;
+const getHasMetabot = (
+  databases: Database[] = [],
+  models: CollectionItem[] = [],
+  isMetabotEnabled = false,
+) => {
+  const hasModels = models.length > 0;
+  const hasSupportedDatabases = databases.some(canUseMetabotOnDatabase);
+  return hasModels && hasSupportedDatabases && isMetabotEnabled;
+};
diff --git a/frontend/src/metabase/home/components/HomePage/HomePage.unit.spec.tsx b/frontend/src/metabase/home/components/HomePage/HomePage.unit.spec.tsx
index 702d5d21d40..d26fd97a4d8 100644
--- a/frontend/src/metabase/home/components/HomePage/HomePage.unit.spec.tsx
+++ b/frontend/src/metabase/home/components/HomePage/HomePage.unit.spec.tsx
@@ -1,45 +1,60 @@
-import { renderWithProviders } from "__support__/ui";
-import * as dom from "metabase/lib/dom";
-import HomePage from "./HomePage";
-
-jest.mock("metabase/lib/dom");
-
-const LayoutMock = () => <div />;
-jest.mock("../HomeLayout", () => LayoutMock);
-
-const ContentMock = () => <div />;
-jest.mock("../../containers/HomeContent", () => ContentMock);
-
-describe("HomePage", () => {
-  let isSmallScreenSpy: jest.SpyInstance;
-
-  beforeEach(() => {
-    isSmallScreenSpy = jest.spyOn(dom, "isSmallScreen");
+import { Route } from "react-router";
+import { DashboardId } from "metabase-types/api";
+import { createMockState } from "metabase-types/store/mocks";
+import { createMockUser } from "metabase-types/api/mocks";
+import { renderWithProviders, screen, waitFor } from "__support__/ui";
+import {
+  setupDatabasesEndpoints,
+  setupPopularItemsEndpoints,
+  setupRecentViewsEndpoints,
+  setupSearchEndpoints,
+} from "__support__/server-mocks";
+import { HomePage } from "./HomePage";
+
+const TEST_DASHBOARD_NAME = "Dashboard";
+
+const TestDashboard = () => <div>{TEST_DASHBOARD_NAME}</div>;
+
+interface SetupOpts {
+  dashboardId?: DashboardId;
+}
+
+const setup = async ({ dashboardId }: SetupOpts = {}) => {
+  const state = createMockState({
+    currentUser: createMockUser({
+      custom_homepage: dashboardId ? { dashboard_id: dashboardId } : null,
+    }),
   });
 
-  afterEach(() => {
-    isSmallScreenSpy.mockRestore();
+  setupDatabasesEndpoints([]);
+  setupSearchEndpoints([]);
+  setupRecentViewsEndpoints([]);
+  setupPopularItemsEndpoints([]);
+
+  renderWithProviders(
+    <>
+      <Route path="/" component={HomePage} />
+      <Route path="/dashboard/:slug" component={TestDashboard} />
+    </>,
+    {
+      withRouter: true,
+      storeInitialState: state,
+    },
+  );
+
+  await waitFor(() => {
+    expect(screen.queryByText(/Loading/i)).not.toBeInTheDocument();
   });
+};
 
-  it("should open the navbar on a regular screen", () => {
-    const onOpenNavbar = jest.fn();
-    isSmallScreenSpy.mockReturnValue(false);
-
-    renderWithProviders(
-      <HomePage hasMetabot={false} onOpenNavbar={onOpenNavbar} />,
-    );
-
-    expect(onOpenNavbar).toHaveBeenCalled();
+describe("HomePage", () => {
+  it("should redirect you to a dashboard when one has been defined to be used as a homepage", async () => {
+    await setup({ dashboardId: 1 });
+    expect(screen.getByText(TEST_DASHBOARD_NAME)).toBeInTheDocument();
   });
 
-  it("should not open the navbar on a small screen", () => {
-    const onOpenNavbar = jest.fn();
-    isSmallScreenSpy.mockReturnValue(true);
-
-    renderWithProviders(
-      <HomePage hasMetabot={false} onOpenNavbar={onOpenNavbar} />,
-    );
-
-    expect(onOpenNavbar).not.toHaveBeenCalled();
+  it("should render the homepage when a custom dashboard is not set", async () => {
+    await setup();
+    expect(screen.queryByText(TEST_DASHBOARD_NAME)).not.toBeInTheDocument();
   });
 });
diff --git a/frontend/src/metabase/home/components/HomePage/index.ts b/frontend/src/metabase/home/components/HomePage/index.ts
index b9bd5af1f99..f5d126e7b2d 100644
--- a/frontend/src/metabase/home/components/HomePage/index.ts
+++ b/frontend/src/metabase/home/components/HomePage/index.ts
@@ -1,2 +1 @@
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export { default } from "./HomePage";
+export * from "./HomePage";
diff --git a/frontend/src/metabase/home/components/HomePopularSection/HomePopularSection.tsx b/frontend/src/metabase/home/components/HomePopularSection/HomePopularSection.tsx
index bbb766cc687..dc1be8fbf22 100644
--- a/frontend/src/metabase/home/components/HomePopularSection/HomePopularSection.tsx
+++ b/frontend/src/metabase/home/components/HomePopularSection/HomePopularSection.tsx
@@ -1,23 +1,26 @@
 import { t } from "ttag";
 import _ from "underscore";
 import * as Urls from "metabase/lib/urls";
-import PopularItems, {
-  getIcon,
-  getName,
-} from "metabase/entities/popular-items";
+import { getIcon, getName } from "metabase/entities/popular-items";
+import { usePopularItemListQuery } from "metabase/common/hooks";
+import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper";
 import { PopularItem } from "metabase-types/api";
-import HomeCaption from "../HomeCaption";
-import HomeHelpCard from "../HomeHelpCard";
-import HomeModelCard from "../HomeModelCard";
+import { HomeCaption } from "../HomeCaption";
+import { HomeHelpCard } from "../HomeHelpCard";
+import { HomeModelCard } from "../HomeModelCard";
 import { SectionBody } from "./HomePopularSection.styled";
 
-export interface HomePopularSectionProps {
-  popularItems: PopularItem[];
-}
+export const HomePopularSection = (): JSX.Element => {
+  const {
+    data: popularItems = [],
+    isLoading,
+    error,
+  } = usePopularItemListQuery();
+
+  if (isLoading || error) {
+    return <LoadingAndErrorWrapper loading={isLoading} error={error} />;
+  }
 
-const HomePopularSection = ({
-  popularItems,
-}: HomePopularSectionProps): JSX.Element => {
   return (
     <div>
       <HomeCaption>{getTitle(popularItems)}</HomeCaption>
@@ -56,6 +59,3 @@ const getTitle = (popularItems: PopularItem[]) => {
       return t`Here are some popular items`;
   }
 };
-
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export default PopularItems.loadList()(HomePopularSection);
diff --git a/frontend/src/metabase/home/components/HomePopularSection/HomePopularSection.unit.spec.tsx b/frontend/src/metabase/home/components/HomePopularSection/HomePopularSection.unit.spec.tsx
index 2c508254eb6..4d52f8876f1 100644
--- a/frontend/src/metabase/home/components/HomePopularSection/HomePopularSection.unit.spec.tsx
+++ b/frontend/src/metabase/home/components/HomePopularSection/HomePopularSection.unit.spec.tsx
@@ -1,51 +1,60 @@
 import { screen } from "@testing-library/react";
 import { createMockPopularItem } from "metabase-types/api/mocks";
-import { renderWithProviders } from "__support__/ui";
+import { renderWithProviders, waitForElementToBeRemoved } from "__support__/ui";
 import { setupPopularItemsEndpoints } from "__support__/server-mocks";
 import { PopularItem } from "metabase-types/api";
-import HomePopularSection from "./HomePopularSection";
+import { HomePopularSection } from "./HomePopularSection";
 
-const setup = (items: PopularItem[]) => {
-  setupPopularItemsEndpoints(items);
+interface SetupOpts {
+  popularItems: PopularItem[];
+}
+
+const setup = async ({ popularItems }: SetupOpts) => {
+  setupPopularItemsEndpoints(popularItems);
   renderWithProviders(<HomePopularSection />);
+  await waitForElementToBeRemoved(() => screen.queryByText(/Loading/i));
 };
 
 describe("HomePopularSection", () => {
   it("should render a list of items of the same type", async () => {
-    setup([
-      createMockPopularItem({
-        model: "dashboard",
-        model_object: {
-          name: "Metrics",
-        },
-      }),
-      createMockPopularItem({
-        model: "dashboard",
-        model_object: {
-          name: "Revenue",
-        },
-      }),
-    ]);
+    await setup({
+      popularItems: [
+        createMockPopularItem({
+          model: "dashboard",
+          model_object: {
+            name: "Metrics",
+          },
+        }),
+        createMockPopularItem({
+          model: "dashboard",
+          model_object: {
+            name: "Revenue",
+          },
+        }),
+      ],
+    });
 
-    expect(await screen.findByText(/popular dashboards/)).toBeInTheDocument();
+    expect(screen.getByText(/popular dashboards/)).toBeInTheDocument();
   });
 
   it("should render a list of items of different types", async () => {
-    setup([
-      createMockPopularItem({
-        model: "dashboard",
-        model_object: {
-          name: "Metrics",
-        },
-      }),
-      createMockPopularItem({
-        model: "card",
-        model_object: {
-          name: "Revenue",
-        },
-      }),
-    ]);
+    await setup({
+      popularItems: [
+        createMockPopularItem({
+          model: "dashboard",
+          model_object: {
+            name: "Metrics",
+          },
+        }),
+        createMockPopularItem({
+          model: "card",
+          model_object: {
+            name: "Revenue",
+          },
+        }),
+      ],
+    });
 
-    expect(await screen.findByText(/popular items/)).toBeInTheDocument();
+    expect(screen.getByText(/popular items/)).toBeInTheDocument();
   });
 });
diff --git a/frontend/src/metabase/home/components/HomePopularSection/index.ts b/frontend/src/metabase/home/components/HomePopularSection/index.ts
index 7a84c0f0ceb..d7e5751760c 100644
--- a/frontend/src/metabase/home/components/HomePopularSection/index.ts
+++ b/frontend/src/metabase/home/components/HomePopularSection/index.ts
@@ -1,2 +1 @@
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export { default } from "./HomePopularSection";
+export * from "./HomePopularSection";
diff --git a/frontend/src/metabase/home/components/HomeRecentSection/HomeRecentSection.tsx b/frontend/src/metabase/home/components/HomeRecentSection/HomeRecentSection.tsx
index d2b6f5d7c99..b8266f9a0df 100644
--- a/frontend/src/metabase/home/components/HomeRecentSection/HomeRecentSection.tsx
+++ b/frontend/src/metabase/home/components/HomeRecentSection/HomeRecentSection.tsx
@@ -1,38 +1,26 @@
-import { connect } from "react-redux";
 import { t } from "ttag";
-import _ from "underscore";
+import { useSelector } from "metabase/lib/redux";
 import * as Urls from "metabase/lib/urls";
-import RecentItems, { getIcon, getName } from "metabase/entities/recent-items";
+import { getIcon, getName } from "metabase/entities/recent-items";
 import { getUser } from "metabase/selectors/user";
-import { RecentItem, User } from "metabase-types/api";
-import { State } from "metabase-types/store";
-import HomeCaption from "../HomeCaption";
-import HomeHelpCard from "../HomeHelpCard";
-import HomeModelCard from "../HomeModelCard";
+import { useRecentItemListQuery } from "metabase/common/hooks";
+import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper";
+import { HomeCaption } from "../HomeCaption";
+import { HomeHelpCard } from "../HomeHelpCard";
+import { HomeModelCard } from "../HomeModelCard";
 import { isWithinWeeks } from "../../utils";
 import { SectionBody } from "./HomeRecentSection.styled";
 
-interface EntityLoaderProps {
-  recentItems: RecentItem[];
-}
-
-interface StateProps {
-  user: User | null;
-}
-
-export type HomeRecentSectionProps = EntityLoaderProps & StateProps;
-
-const mapStateToProps = (state: State): StateProps => ({
-  user: getUser(state),
-});
-
-const HomeRecentSection = ({
-  user,
-  recentItems,
-}: HomeRecentSectionProps): JSX.Element => {
+export const HomeRecentSection = () => {
+  const { data: recentItems = [], isLoading, error } = useRecentItemListQuery();
+  const user = useSelector(getUser);
   const hasHelpCard =
     user != null && user.is_installer && isWithinWeeks(user.first_login, 2);
 
+  if (isLoading || error) {
+    return <LoadingAndErrorWrapper loading={isLoading} error={error} />;
+  }
+
   return (
     <div>
       <HomeCaption>{t`Pick up where you left off`}</HomeCaption>
@@ -50,9 +38,3 @@ const HomeRecentSection = ({
     </div>
   );
 };
-
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export default _.compose(
-  RecentItems.loadList(),
-  connect(mapStateToProps),
-)(HomeRecentSection);
diff --git a/frontend/src/metabase/home/components/HomeRecentSection/HomeRecentSection.unit.spec.tsx b/frontend/src/metabase/home/components/HomeRecentSection/HomeRecentSection.unit.spec.tsx
index cf6039b4615..2c0580d434f 100644
--- a/frontend/src/metabase/home/components/HomeRecentSection/HomeRecentSection.unit.spec.tsx
+++ b/frontend/src/metabase/home/components/HomeRecentSection/HomeRecentSection.unit.spec.tsx
@@ -3,14 +3,13 @@ import { createMockRecentItem, createMockUser } from "metabase-types/api/mocks";
 import { renderWithProviders } from "__support__/ui";
 import { setupRecentViewsEndpoints } from "__support__/server-mocks";
 import { User } from "metabase-types/api";
-import * as utils from "../../utils";
-import HomeRecentSection from "./HomeRecentSection";
+import { HomeRecentSection } from "./HomeRecentSection";
 
-jest.mock("../../utils", () => ({
-  isWithinWeeks: jest.fn(),
-}));
+interface SetupOpts {
+  user?: User;
+}
 
-const setup = async (user?: User) => {
+const setup = async ({ user = createMockUser() }: SetupOpts = {}) => {
   setupRecentViewsEndpoints([
     createMockRecentItem({
       model: "table",
@@ -30,28 +29,35 @@ const setup = async (user?: User) => {
 };
 
 describe("HomeRecentSection", () => {
+  beforeEach(() => {
+    jest.useFakeTimers({ advanceTimers: true });
+    jest.setSystemTime(new Date(2020, 0, 4));
+  });
+
   afterEach(() => {
+    jest.useRealTimers();
     jest.restoreAllMocks();
   });
 
   describe("new installers", () => {
     it("should show a help link for new installers", async () => {
-      jest.spyOn(utils, "isWithinWeeks").mockImplementationOnce(() => true);
-
-      await setup(
-        createMockUser({
+      await setup({
+        user: createMockUser({
           is_installer: true,
           first_login: "2020-01-05T00:00:00Z",
         }),
-      );
+      });
 
-      expect(await screen.findByText("Metabase tips")).toBeInTheDocument();
+      expect(screen.getByText("Metabase tips")).toBeInTheDocument();
     });
 
     it("should not show a help link for regular users", async () => {
-      jest.spyOn(utils, "isWithinWeeks").mockImplementationOnce(() => false);
-
-      await setup();
+      await setup({
+        user: createMockUser({
+          is_installer: false,
+          first_login: "2019-11-05T00:00:00Z",
+        }),
+      });
 
       expect(screen.queryByText("Metabase tips")).not.toBeInTheDocument();
     });
@@ -60,9 +66,7 @@ describe("HomeRecentSection", () => {
   it("should render a list of recent items", async () => {
     await setup();
 
-    expect(
-      await screen.findByText("Pick up where you left off"),
-    ).toBeInTheDocument();
+    expect(screen.getByText("Pick up where you left off")).toBeInTheDocument();
     expect(screen.getByText("Orders")).toBeInTheDocument();
   });
 });
diff --git a/frontend/src/metabase/home/components/HomeRecentSection/index.ts b/frontend/src/metabase/home/components/HomeRecentSection/index.ts
index 0efb9a3d7c1..8bf83463d64 100644
--- a/frontend/src/metabase/home/components/HomeRecentSection/index.ts
+++ b/frontend/src/metabase/home/components/HomeRecentSection/index.ts
@@ -1,2 +1 @@
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export { default } from "./HomeRecentSection";
+export * from "./HomeRecentSection";
diff --git a/frontend/src/metabase/home/components/HomeXrayCard/HomeXrayCard.tsx b/frontend/src/metabase/home/components/HomeXrayCard/HomeXrayCard.tsx
index 29da2174560..742c6272611 100644
--- a/frontend/src/metabase/home/components/HomeXrayCard/HomeXrayCard.tsx
+++ b/frontend/src/metabase/home/components/HomeXrayCard/HomeXrayCard.tsx
@@ -1,4 +1,4 @@
-import HomeCard from "../HomeCard";
+import { HomeCard } from "../HomeCard";
 import {
   CardIcon,
   CardIconContainer,
@@ -7,13 +7,13 @@ import {
   CardTitleSecondary,
 } from "./HomeXrayCard.styled";
 
-export interface HomeXrayCardProps {
+interface HomeXrayCardProps {
   title: string;
   url: string;
   message: string;
 }
 
-const HomeXrayCard = ({
+export const HomeXrayCard = ({
   title,
   url,
   message,
@@ -30,6 +30,3 @@ const HomeXrayCard = ({
     </HomeCard>
   );
 };
-
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export default HomeXrayCard;
diff --git a/frontend/src/metabase/home/components/HomeXrayCard/HomeXrayCard.unit.spec.tsx b/frontend/src/metabase/home/components/HomeXrayCard/HomeXrayCard.unit.spec.tsx
index e248df95702..51b5c4748e0 100644
--- a/frontend/src/metabase/home/components/HomeXrayCard/HomeXrayCard.unit.spec.tsx
+++ b/frontend/src/metabase/home/components/HomeXrayCard/HomeXrayCard.unit.spec.tsx
@@ -1,23 +1,25 @@
-import { render, screen } from "@testing-library/react";
-import HomeXrayCard, { HomeXrayCardProps } from "./HomeXrayCard";
+import { render, screen } from "__support__/ui";
+import { HomeXrayCard } from "./HomeXrayCard";
+
+interface SetupOpts {
+  title: string;
+  message: string;
+  url: string;
+}
+
+const setup = ({ title, message, url }: SetupOpts) => {
+  render(<HomeXrayCard title={title} message={message} url={url} />);
+};
 
 describe("HomeXrayCard", () => {
   it("should render correctly", () => {
-    const props = getProps({
+    setup({
       title: "Orders",
       message: "A look at",
+      url: "/question/1",
     });
 
-    render(<HomeXrayCard {...props} />);
-
     expect(screen.getByText("Orders")).toBeInTheDocument();
     expect(screen.getByText("A look at")).toBeInTheDocument();
   });
 });
-
-const getProps = (opts?: Partial<HomeXrayCardProps>): HomeXrayCardProps => ({
-  title: "Orders",
-  message: "A look at",
-  url: "/question/1",
-  ...opts,
-});
diff --git a/frontend/src/metabase/home/components/HomeXrayCard/index.ts b/frontend/src/metabase/home/components/HomeXrayCard/index.ts
index 1d14ac702e4..f3d248ac0a0 100644
--- a/frontend/src/metabase/home/components/HomeXrayCard/index.ts
+++ b/frontend/src/metabase/home/components/HomeXrayCard/index.ts
@@ -1,2 +1 @@
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export { default } from "./HomeXrayCard";
+export * from "./HomeXrayCard";
diff --git a/frontend/src/metabase/home/components/HomeXraySection/HomeXraySection.tsx b/frontend/src/metabase/home/components/HomeXraySection/HomeXraySection.tsx
index 67a60d178c6..7ec7fb81eac 100644
--- a/frontend/src/metabase/home/components/HomeXraySection/HomeXraySection.tsx
+++ b/frontend/src/metabase/home/components/HomeXraySection/HomeXraySection.tsx
@@ -2,12 +2,18 @@ import { ChangeEvent, useCallback, useMemo, useState } from "react";
 import _ from "underscore";
 import { t } from "ttag";
 import * as Urls from "metabase/lib/urls";
+import { isSyncCompleted } from "metabase/lib/syncing";
 import Select from "metabase/core/components/Select";
+import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper";
+import {
+  useDatabaseCandidateListQuery,
+  useDatabaseListQuery,
+} from "metabase/common/hooks";
 import { DatabaseCandidate } from "metabase-types/api";
 import Database from "metabase-lib/metadata/Database";
-import HomeCaption from "../HomeCaption";
-import HomeHelpCard from "../HomeHelpCard";
-import HomeXrayCard from "../HomeXrayCard";
+import { HomeCaption } from "../HomeCaption";
+import { HomeHelpCard } from "../HomeHelpCard";
+import { HomeXrayCard } from "../HomeXrayCard";
 import {
   DatabaseLinkIcon,
   DatabaseLink,
@@ -18,15 +24,35 @@ import {
   SchemaTriggerIcon,
 } from "./HomeXraySection.styled";
 
-export interface HomeXraySectionProps {
+export const HomeXraySection = () => {
+  const databaseListState = useDatabaseListQuery();
+  const database = getXrayDatabase(databaseListState.data);
+  const candidateListState = useDatabaseCandidateListQuery({
+    query: database ? { id: database.id } : undefined,
+    enabled: database != null,
+  });
+  const isLoading = databaseListState.isLoading || candidateListState.isLoading;
+  const error = databaseListState.error ?? candidateListState.error;
+
+  if (isLoading || error) {
+    return <LoadingAndErrorWrapper loading={isLoading} error={error} />;
+  }
+
+  if (!database) {
+    return null;
+  }
+
+  return (
+    <HomeXrayView database={database} candidates={candidateListState.data} />
+  );
+};
+
+interface HomeXrayViewProps {
   database: Database;
-  candidates: DatabaseCandidate[];
+  candidates?: DatabaseCandidate[];
 }
 
-const HomeXraySection = ({
-  database,
-  candidates,
-}: HomeXraySectionProps): JSX.Element => {
+const HomeXrayView = ({ database, candidates = [] }: HomeXrayViewProps) => {
   const isSample = database.is_sample;
   const schemas = candidates.map(d => d.schema);
   const [schema, setSchema] = useState(schemas[0]);
@@ -119,6 +145,12 @@ const DatabaseInfo = ({ database }: DatabaseInfoProps) => {
   );
 };
 
+const getXrayDatabase = (databases: Database[] = []) => {
+  const sampleDatabase = databases.find(d => d.is_sample && isSyncCompleted(d));
+  const userDatabase = databases.find(d => !d.is_sample && isSyncCompleted(d));
+  return userDatabase ?? sampleDatabase;
+};
+
 const getMessages = (count: number) => {
   const options = [
     t`A look at`,
@@ -137,6 +169,3 @@ const getMessages = (count: number) => {
 const getSchemaOption = (schema: string) => {
   return schema;
 };
-
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export default HomeXraySection;
diff --git a/frontend/src/metabase/home/components/HomeXraySection/HomeXraySection.unit.spec.tsx b/frontend/src/metabase/home/components/HomeXraySection/HomeXraySection.unit.spec.tsx
index 9748a45888e..9d38d94b0d2 100644
--- a/frontend/src/metabase/home/components/HomeXraySection/HomeXraySection.unit.spec.tsx
+++ b/frontend/src/metabase/home/components/HomeXraySection/HomeXraySection.unit.spec.tsx
@@ -1,49 +1,43 @@
-import { checkNotNull } from "metabase/core/utils/types";
-import { getMetadata } from "metabase/selectors/metadata";
+import { Database, DatabaseCandidate } from "metabase-types/api";
 import {
   createMockDatabase,
   createMockDatabaseCandidate,
   createMockTableCandidate,
 } from "metabase-types/api/mocks";
-import { Database, DatabaseCandidate } from "metabase-types/api";
-import { createMockState } from "metabase-types/store/mocks";
-import { createMockEntitiesState } from "__support__/store";
-import { renderWithProviders, screen } from "__support__/ui";
-import HomeXraySection from "./HomeXraySection";
+import {
+  setupDatabaseCandidatesEndpoint,
+  setupDatabasesEndpoints,
+} from "__support__/server-mocks";
+import {
+  renderWithProviders,
+  screen,
+  waitForElementToBeRemoved,
+} from "__support__/ui";
+import { HomeXraySection } from "./HomeXraySection";
 
 interface SetupOpts {
   database: Database;
   candidates: DatabaseCandidate[];
 }
 
-const setup = ({ database, candidates }: SetupOpts) => {
-  const state = createMockState({
-    entities: createMockEntitiesState({ databases: [database] }),
-  });
-  const metadata = getMetadata(state);
-
-  renderWithProviders(
-    <HomeXraySection
-      database={checkNotNull(metadata.database(database.id))}
-      candidates={candidates}
-    />,
-    {
-      storeInitialState: state,
-    },
-  );
+const setup = async ({ database, candidates }: SetupOpts) => {
+  setupDatabasesEndpoints([database]);
+  setupDatabaseCandidatesEndpoint(database.id, candidates);
+  renderWithProviders(<HomeXraySection />);
+  await waitForElementToBeRemoved(() => screen.queryByText(/Loading/i));
 };
 
 describe("HomeXraySection", () => {
-  it("should show x-rays for a sample database", () => {
-    setup({
+  it("should show x-rays for a sample database", async () => {
+    await setup({
       database: createMockDatabase({
         is_sample: true,
       }),
       candidates: [
         createMockDatabaseCandidate({
           tables: [
-            createMockTableCandidate({ title: "Orders" }),
-            createMockTableCandidate({ title: "People" }),
+            createMockTableCandidate({ title: "Orders", url: "/auto/1" }),
+            createMockTableCandidate({ title: "People", url: "/auto/2" }),
           ],
         }),
       ],
@@ -54,18 +48,20 @@ describe("HomeXraySection", () => {
     expect(screen.getByText("People")).toBeInTheDocument();
   });
 
-  it("should show x-rays for a user database", () => {
-    setup({
+  it("should show x-rays for a user database", async () => {
+    await setup({
       database: createMockDatabase({
         name: "H2",
         is_sample: false,
       }),
       candidates: [
         createMockDatabaseCandidate({
+          id: "1/public",
           schema: "public",
           tables: [createMockTableCandidate({ title: "Orders" })],
         }),
         createMockDatabaseCandidate({
+          id: "1/internal",
           schema: "internal",
           tables: [createMockTableCandidate({ title: "People" })],
         }),
diff --git a/frontend/src/metabase/home/components/HomeXraySection/index.ts b/frontend/src/metabase/home/components/HomeXraySection/index.ts
index e0f5cc71d03..23cf5ffaa97 100644
--- a/frontend/src/metabase/home/components/HomeXraySection/index.ts
+++ b/frontend/src/metabase/home/components/HomeXraySection/index.ts
@@ -1,2 +1 @@
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export { default } from "./HomeXraySection";
+export * from "./HomeXraySection";
diff --git a/frontend/src/metabase/home/containers/HomeContent/HomeContent.tsx b/frontend/src/metabase/home/containers/HomeContent/HomeContent.tsx
deleted file mode 100644
index aa38fb9cc7a..00000000000
--- a/frontend/src/metabase/home/containers/HomeContent/HomeContent.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { connect } from "react-redux";
-import _ from "underscore";
-import Databases from "metabase/entities/databases";
-import RecentItems from "metabase/entities/recent-items";
-import PopularItems from "metabase/entities/popular-items";
-import { getUser } from "metabase/selectors/user";
-import { State } from "metabase-types/store";
-import { getSetting } from "metabase/selectors/settings";
-import HomeContent from "../../components/HomeContent";
-
-const mapStateToProps = (state: State) => ({
-  user: getUser(state),
-  isXrayEnabled: getSetting(state, "enable-xrays"),
-});
-
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export default _.compose(
-  Databases.loadList({ loadingAndErrorWrapper: false }),
-  RecentItems.loadList({ reload: true, loadingAndErrorWrapper: false }),
-  PopularItems.loadList({ reload: true, loadingAndErrorWrapper: false }),
-  connect(mapStateToProps),
-)(HomeContent);
diff --git a/frontend/src/metabase/home/containers/HomeContent/index.ts b/frontend/src/metabase/home/containers/HomeContent/index.ts
deleted file mode 100644
index acc76cd9ddd..00000000000
--- a/frontend/src/metabase/home/containers/HomeContent/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export { default } from "./HomeContent";
diff --git a/frontend/src/metabase/home/containers/HomePageApp/HomePageApp.tsx b/frontend/src/metabase/home/containers/HomePageApp/HomePageApp.tsx
deleted file mode 100644
index addf98af2a5..00000000000
--- a/frontend/src/metabase/home/containers/HomePageApp/HomePageApp.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import { connect } from "react-redux";
-import _ from "underscore";
-import Databases from "metabase/entities/databases";
-import Search from "metabase/entities/search";
-import { openNavbar } from "metabase/redux/app";
-import { getSetting } from "metabase/selectors/settings";
-import { getUserRedirectHomepageToDashboard } from "metabase/selectors/user";
-import { CollectionItem } from "metabase-types/api";
-import { State } from "metabase-types/store";
-import { canUseMetabotOnDatabase } from "metabase/metabot/utils";
-import Database from "metabase-lib/metadata/Database";
-import HomePage from "../../components/HomePage";
-
-interface EntityLoaderProps {
-  databases: Database[];
-  models: CollectionItem[];
-}
-
-interface StateProps {
-  hasMetabot: boolean;
-  homepageDashboard: number | null;
-}
-
-const mapStateToProps = (
-  state: State,
-  { databases, models }: EntityLoaderProps,
-): StateProps => {
-  const hasModels = models.length > 0;
-  const hasSupportedDatabases = databases.some(canUseMetabotOnDatabase);
-  const isMetabotEnabled = getSetting(state, "is-metabot-enabled");
-
-  return {
-    hasMetabot: hasModels && hasSupportedDatabases && isMetabotEnabled,
-    homepageDashboard: getUserRedirectHomepageToDashboard(state),
-  };
-};
-
-const mapDispatchToProps = {
-  onOpenNavbar: openNavbar,
-};
-
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export default _.compose(
-  Databases.loadList(),
-  Search.loadList({
-    query: {
-      models: "dataset",
-      limit: 1,
-    },
-    listName: "models",
-  }),
-  connect(mapStateToProps, mapDispatchToProps),
-)(HomePage);
diff --git a/frontend/src/metabase/home/containers/HomePageApp/HomePageApp.unit.spec.tsx b/frontend/src/metabase/home/containers/HomePageApp/HomePageApp.unit.spec.tsx
deleted file mode 100644
index f0f94012b0a..00000000000
--- a/frontend/src/metabase/home/containers/HomePageApp/HomePageApp.unit.spec.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { Route, Router } from "react-router";
-import fetchMock from "fetch-mock";
-import DashboardApp from "metabase/dashboard/containers/DashboardApp";
-
-import { createMockDashboard, createMockUser } from "metabase-types/api/mocks";
-import { createMockState } from "metabase-types/store/mocks";
-
-import { renderWithProviders, screen } from "__support__/ui";
-import HomePageApp from "./HomePageApp";
-
-const LayoutMock = () => <div>Content</div>;
-jest.mock("../../components/HomeLayout", () => LayoutMock);
-
-const DashboardMock = () => <div>Dashboard</div>;
-jest.mock("metabase/dashboard/containers/DashboardApp", () => DashboardMock);
-
-const setupForRedirect = ({ customDashboardSet = true } = {}) => {
-  const dashboard = createMockDashboard();
-
-  fetchMock.get("path:/api/dashboard/1", dashboard);
-  fetchMock.get("path:/api/database", []);
-  fetchMock.get("path:/api/search", []);
-
-  renderWithProviders(
-    <Router>
-      <Route path="/" component={HomePageApp} />
-      <Route path="/dashboard/:slug" component={DashboardApp} />
-    </Router>,
-    {
-      withRouter: true,
-      storeInitialState: createMockState({
-        currentUser: createMockUser({
-          custom_homepage: customDashboardSet
-            ? {
-                dashboard_id: 1,
-              }
-            : null,
-        }),
-      }),
-    },
-  );
-};
-
-it("should redirect you to a dashboard when one has been defined to be used as a homepage", async () => {
-  setupForRedirect();
-  expect(await screen.findByText("Dashboard")).toBeInTheDocument();
-});
-
-it("should render homepage when custom-homepage is false", async () => {
-  setupForRedirect({
-    customDashboardSet: false,
-  });
-  expect(await screen.findByText("Content")).toBeInTheDocument();
-});
diff --git a/frontend/src/metabase/home/containers/HomePageApp/index.ts b/frontend/src/metabase/home/containers/HomePageApp/index.ts
deleted file mode 100644
index a4c0203dd9a..00000000000
--- a/frontend/src/metabase/home/containers/HomePageApp/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export { default } from "./HomePageApp";
diff --git a/frontend/src/metabase/home/containers/HomeXraySection/HomeXraySection.tsx b/frontend/src/metabase/home/containers/HomeXraySection/HomeXraySection.tsx
deleted file mode 100644
index a4d002840bf..00000000000
--- a/frontend/src/metabase/home/containers/HomeXraySection/HomeXraySection.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { connect } from "react-redux";
-import _ from "underscore";
-import Databases from "metabase/entities/databases";
-import DatabaseCandidates from "metabase/entities/database-candidates";
-import { isSyncCompleted } from "metabase/lib/syncing";
-import { State } from "metabase-types/store";
-import Database from "metabase-lib/metadata/Database";
-import HomeXraySection from "../../components/HomeXraySection";
-
-interface XraySectionProps {
-  database?: Database;
-  databases: Database[];
-}
-
-const getXrayDatabase = ({ databases }: XraySectionProps) => {
-  const sampleDatabase = databases.find(d => d.is_sample && isSyncCompleted(d));
-  const userDatabase = databases.find(d => !d.is_sample && isSyncCompleted(d));
-  return userDatabase ?? sampleDatabase;
-};
-
-const getXrayQuery = (state: State, { database }: XraySectionProps) => ({
-  id: database?.id,
-});
-
-const mapStateToProps = (state: State, props: XraySectionProps) => ({
-  database: getXrayDatabase(props),
-});
-
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export default _.compose(
-  Databases.loadList(),
-  connect(mapStateToProps),
-  DatabaseCandidates.loadList({ query: getXrayQuery, listName: "candidates" }),
-)(HomeXraySection);
diff --git a/frontend/src/metabase/home/containers/HomeXraySection/index.ts b/frontend/src/metabase/home/containers/HomeXraySection/index.ts
deleted file mode 100644
index e0f5cc71d03..00000000000
--- a/frontend/src/metabase/home/containers/HomeXraySection/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-// eslint-disable-next-line import/no-default-export -- deprecated usage
-export { default } from "./HomeXraySection";
diff --git a/frontend/src/metabase/home/selectors.ts b/frontend/src/metabase/home/selectors.ts
new file mode 100644
index 00000000000..5e499dced0a
--- /dev/null
+++ b/frontend/src/metabase/home/selectors.ts
@@ -0,0 +1,25 @@
+import { createSelector } from "@reduxjs/toolkit";
+import { getSetting } from "metabase/selectors/settings";
+import { State } from "metabase-types/store";
+import { getUser } from "metabase/selectors/user";
+
+export const getIsXrayEnabled = (state: State) => {
+  return getSetting(state, "enable-xrays");
+};
+
+export const getIsMetabotEnabled = (state: State) => {
+  return getSetting(state, "is-metabot-enabled");
+};
+
+export const getHasMetabotLogo = (state: State) => {
+  return getSetting(state, "show-metabot");
+};
+
+export const getHasIllustration = (state: State) => {
+  return getSetting(state, "show-lighthouse-illustration");
+};
+
+export const getCustomHomePageDashboardId = createSelector(
+  [getUser],
+  user => user?.custom_homepage?.dashboard_id || null,
+);
diff --git a/frontend/src/metabase/routes.jsx b/frontend/src/metabase/routes.jsx
index 0e62f2aee1b..dbfcd02420c 100644
--- a/frontend/src/metabase/routes.jsx
+++ b/frontend/src/metabase/routes.jsx
@@ -55,7 +55,7 @@ import DashboardMoveModal from "metabase/dashboard/components/DashboardMoveModal
 import DashboardCopyModal from "metabase/dashboard/components/DashboardCopyModal";
 import { ModalRoute } from "metabase/hoc/ModalRoute";
 
-import HomePageApp from "metabase/home/containers/HomePageApp";
+import { HomePage } from "metabase/home/components/HomePage";
 import CollectionLanding from "metabase/collections/components/CollectionLanding";
 
 import ArchiveApp from "metabase/archive/containers/ArchiveApp";
@@ -120,7 +120,7 @@ export const getRoutes = store => (
         {/* The global all hands routes, things in here are for all the folks */}
         <Route
           path="/"
-          component={HomePageApp}
+          component={HomePage}
           onEnter={(nextState, replace) => {
             const page = PLUGIN_LANDING_PAGE[0] && PLUGIN_LANDING_PAGE[0]();
             if (page && page !== "/") {
diff --git a/frontend/src/metabase/selectors/user.ts b/frontend/src/metabase/selectors/user.ts
index 59a1585c7d4..0f82edad0ed 100644
--- a/frontend/src/metabase/selectors/user.ts
+++ b/frontend/src/metabase/selectors/user.ts
@@ -30,8 +30,3 @@ export const getUserPersonalCollectionId = createSelector(
   [getUser],
   user => user?.personal_collection_id,
 );
-
-export const getUserRedirectHomepageToDashboard = createSelector(
-  [getUser],
-  user => user?.custom_homepage?.dashboard_id || null,
-);
-- 
GitLab