From 9f4591dbb345a96697e953e3c830d433a5c4401e Mon Sep 17 00:00:00 2001
From: Alexander Polyankin <alexander.polyankin@metabase.com>
Date: Thu, 7 Apr 2022 22:49:15 +0400
Subject: [PATCH] Cleanup new homepage (#21448)

---
 frontend/src/metabase-types/api/activity.ts   |   4 +-
 .../src/metabase-types/api/mocks/activity.ts  |  14 +--
 frontend/src/metabase-types/api/mocks/user.ts |   1 +
 frontend/src/metabase-types/api/user.ts       |   1 +
 frontend/src/metabase/entities/index.js       |   4 +-
 .../{popular-views.js => popular-items.js}    |  14 +--
 .../{recent-views.js => recent-items.js}      |  12 +-
 .../HomeCaption/HomeCaption.styled.tsx        |  20 +++
 .../components/HomeCaption/HomeCaption.tsx    |  13 ++
 .../HomeCaption/HomeCaption.unit.spec.tsx     |  11 ++
 .../homepage/components/HomeCaption/index.ts  |   1 +
 .../components/HomeCard/HomeCard.styled.tsx   |   9 +-
 .../HomeCard/HomeCard.unit.spec.tsx           |  11 ++
 .../components/HomeContent/HomeContent.tsx    |  18 ++-
 .../HomeContent/HomeContent.unit.spec.tsx     | 115 ++++++++++++++++++
 .../HomeGreeting/HomeGreeting.styled.tsx      |   9 ++
 .../HomeGreeting/HomeGreeting.unit.spec.tsx   |  36 ++++++
 .../HomeHelpCard/HomeHelpCard.styled.tsx      |   5 +
 .../HomeHelpCard/HomeHelpCard.unit.spec.tsx   |  11 ++
 .../HomeLayout/HomeLayout.styled.tsx          |  29 ++++-
 .../HomeLayout/HomeLayout.unit.spec.tsx       |  21 ++++
 .../HomeModelCard/HomeModelCard.unit.spec.tsx |  24 ++++
 .../HomePage/HomePage.unit.spec.tsx           |  42 +++++++
 .../HomePopularSection.styled.tsx             |  12 --
 .../HomePopularSection/HomePopularSection.tsx |  23 ++--
 .../HomePopularSection.unit.spec.tsx          |  61 ++++++++++
 .../HomeRecentSection.styled.tsx              |   9 --
 .../HomeRecentSection/HomeRecentSection.tsx   |  15 +--
 .../HomeRecentSection.unit.spec.tsx           |  31 +++++
 .../HomeXrayCard/HomeXrayCard.unit.spec.tsx   |  24 ++++
 .../HomeXraySection.styled.tsx                |  31 +++--
 .../HomeXraySection/HomeXraySection.tsx       | 113 +++++++++++++----
 .../HomeXraySection.unit.spec.tsx             |  66 ++++++++++
 .../containers/HomeContent/HomeContent.tsx    |   4 +-
 .../HomePopularSection/HomePopularSection.tsx |   4 +-
 .../HomeRecentSection/HomeRecentSection.tsx   |   4 +-
 .../HomeXraySection/HomeXraySection.tsx       |   2 +-
 .../metabase/nav/components/RecentsList.jsx   |   4 +-
 frontend/src/metabase/schema.js               |   4 +-
 .../onboarding/home/homepage.cy.spec.js       |  97 ++++++++++++++-
 .../test/snapshot-creators/default.cy.snap.js |   1 +
 41 files changed, 807 insertions(+), 123 deletions(-)
 rename frontend/src/metabase/entities/{popular-views.js => popular-items.js} (73%)
 rename frontend/src/metabase/entities/{recent-views.js => recent-items.js} (78%)
 create mode 100644 frontend/src/metabase/home/homepage/components/HomeCaption/HomeCaption.styled.tsx
 create mode 100644 frontend/src/metabase/home/homepage/components/HomeCaption/HomeCaption.tsx
 create mode 100644 frontend/src/metabase/home/homepage/components/HomeCaption/HomeCaption.unit.spec.tsx
 create mode 100644 frontend/src/metabase/home/homepage/components/HomeCaption/index.ts
 create mode 100644 frontend/src/metabase/home/homepage/components/HomeCard/HomeCard.unit.spec.tsx
 create mode 100644 frontend/src/metabase/home/homepage/components/HomeContent/HomeContent.unit.spec.tsx
 create mode 100644 frontend/src/metabase/home/homepage/components/HomeGreeting/HomeGreeting.unit.spec.tsx
 create mode 100644 frontend/src/metabase/home/homepage/components/HomeHelpCard/HomeHelpCard.unit.spec.tsx
 create mode 100644 frontend/src/metabase/home/homepage/components/HomeLayout/HomeLayout.unit.spec.tsx
 create mode 100644 frontend/src/metabase/home/homepage/components/HomeModelCard/HomeModelCard.unit.spec.tsx
 create mode 100644 frontend/src/metabase/home/homepage/components/HomePage/HomePage.unit.spec.tsx
 create mode 100644 frontend/src/metabase/home/homepage/components/HomePopularSection/HomePopularSection.unit.spec.tsx
 create mode 100644 frontend/src/metabase/home/homepage/components/HomeRecentSection/HomeRecentSection.unit.spec.tsx
 create mode 100644 frontend/src/metabase/home/homepage/components/HomeXrayCard/HomeXrayCard.unit.spec.tsx
 create mode 100644 frontend/src/metabase/home/homepage/components/HomeXraySection/HomeXraySection.unit.spec.tsx

diff --git a/frontend/src/metabase-types/api/activity.ts b/frontend/src/metabase-types/api/activity.ts
index 36aea637e19..c84572be246 100644
--- a/frontend/src/metabase-types/api/activity.ts
+++ b/frontend/src/metabase-types/api/activity.ts
@@ -4,12 +4,12 @@ export interface ModelObject {
   name: string;
 }
 
-export interface RecentView {
+export interface RecentItem {
   model: ModelType;
   model_object: ModelObject;
 }
 
-export interface PopularView {
+export interface PopularItem {
   model: ModelType;
   model_object: ModelObject;
 }
diff --git a/frontend/src/metabase-types/api/mocks/activity.ts b/frontend/src/metabase-types/api/mocks/activity.ts
index 22a0d6d0321..c54c2ceee91 100644
--- a/frontend/src/metabase-types/api/mocks/activity.ts
+++ b/frontend/src/metabase-types/api/mocks/activity.ts
@@ -1,4 +1,4 @@
-import { ModelObject, PopularView, RecentView } from "metabase-types/api";
+import { ModelObject, PopularItem, RecentItem } from "metabase-types/api";
 
 export const createMockModelObject = (
   opts?: Partial<ModelObject>,
@@ -7,17 +7,17 @@ export const createMockModelObject = (
   ...opts,
 });
 
-export const createMockRecentView = (
-  opts?: Partial<RecentView>,
-): RecentView => ({
+export const createMockRecentItem = (
+  opts?: Partial<RecentItem>,
+): RecentItem => ({
   model: "table",
   model_object: createMockModelObject(),
   ...opts,
 });
 
-export const createMockPopularView = (
-  opts?: Partial<PopularView>,
-): PopularView => ({
+export const createMockPopularItem = (
+  opts?: Partial<PopularItem>,
+): PopularItem => ({
   model: "table",
   model_object: createMockModelObject(),
   ...opts,
diff --git a/frontend/src/metabase-types/api/mocks/user.ts b/frontend/src/metabase-types/api/mocks/user.ts
index 6f024d06629..d77823344cb 100644
--- a/frontend/src/metabase-types/api/mocks/user.ts
+++ b/frontend/src/metabase-types/api/mocks/user.ts
@@ -15,6 +15,7 @@ export const createMockUser = (opts?: Partial<User>): User => ({
   has_question_and_dashboard: false,
   personal_collection_id: 1,
   date_joined: new Date().toISOString(),
+  first_login: new Date().toISOString(),
   last_login: new Date().toISOString(),
   ...opts,
 });
diff --git a/frontend/src/metabase-types/api/user.ts b/frontend/src/metabase-types/api/user.ts
index 718feb28649..275bf70f4f7 100644
--- a/frontend/src/metabase-types/api/user.ts
+++ b/frontend/src/metabase-types/api/user.ts
@@ -12,6 +12,7 @@ export interface User {
   has_invited_second_user: boolean;
   has_question_and_dashboard: boolean;
   date_joined: string;
+  first_login: string;
   last_login: string;
   personal_collection_id: number;
 }
diff --git a/frontend/src/metabase/entities/index.js b/frontend/src/metabase/entities/index.js
index 96383657e90..2061c15cf1e 100644
--- a/frontend/src/metabase/entities/index.js
+++ b/frontend/src/metabase/entities/index.js
@@ -24,6 +24,6 @@ export { default as users } from "./users";
 export { default as groups } from "./groups";
 
 export { default as search } from "./search";
-export { default as popularViews } from "./popular-views";
-export { default as recentViews } from "./recent-views";
+export { default as recentItems } from "./recent-items";
+export { default as popularItems } from "./popular-items";
 export { default as snippets } from "./snippets";
diff --git a/frontend/src/metabase/entities/popular-views.js b/frontend/src/metabase/entities/popular-items.js
similarity index 73%
rename from frontend/src/metabase/entities/popular-views.js
rename to frontend/src/metabase/entities/popular-items.js
index 6102930ffcb..a6b5805c69a 100644
--- a/frontend/src/metabase/entities/popular-views.js
+++ b/frontend/src/metabase/entities/popular-items.js
@@ -1,6 +1,6 @@
 import { createEntity } from "metabase/lib/entities";
 import { entityTypeForObject } from "metabase/lib/schema";
-import { PopularViewsSchema } from "metabase/schema";
+import { PopularItemSchema } from "metabase/schema";
 
 export const getEntity = item => {
   const entities = require("metabase/entities");
@@ -16,11 +16,11 @@ export const getIcon = item => {
   return entity.objectSelectors.getIcon(item.model_object);
 };
 
-const PopularViews = createEntity({
-  name: "popularViews",
-  nameOne: "popularView",
-  path: "/api/activity/popular_views",
-  schema: PopularViewsSchema,
+const PopularItems = createEntity({
+  name: "popularItems",
+  nameOne: "popularItem",
+  path: "/api/activity/popular_items",
+  schema: PopularItemSchema,
 
   wrapEntity(item, dispatch = null) {
     const entity = getEntity(item);
@@ -33,4 +33,4 @@ const PopularViews = createEntity({
   },
 });
 
-export default PopularViews;
+export default PopularItems;
diff --git a/frontend/src/metabase/entities/recent-views.js b/frontend/src/metabase/entities/recent-items.js
similarity index 78%
rename from frontend/src/metabase/entities/recent-views.js
rename to frontend/src/metabase/entities/recent-items.js
index 594bbfb0cdc..0687a5089a2 100644
--- a/frontend/src/metabase/entities/recent-views.js
+++ b/frontend/src/metabase/entities/recent-items.js
@@ -1,6 +1,6 @@
 import { createEntity } from "metabase/lib/entities";
 import { entityTypeForObject } from "metabase/lib/schema";
-import { RecentViewsSchema } from "metabase/schema";
+import { RecentItemSchema } from "metabase/schema";
 
 export const getEntity = item => {
   const entities = require("metabase/entities");
@@ -16,11 +16,11 @@ export const getIcon = item => {
   return entity.objectSelectors.getIcon(item.model_object);
 };
 
-const RecentViews = createEntity({
-  name: "recentViews",
-  nameOne: "recentView",
+const RecentItems = createEntity({
+  name: "recentItems",
+  nameOne: "recentItem",
   path: "/api/activity/recent_views",
-  schema: RecentViewsSchema,
+  schema: RecentItemSchema,
 
   wrapEntity(item, dispatch = null) {
     const entity = getEntity(item);
@@ -33,4 +33,4 @@ const RecentViews = createEntity({
   },
 });
 
-export default RecentViews;
+export default RecentItems;
diff --git a/frontend/src/metabase/home/homepage/components/HomeCaption/HomeCaption.styled.tsx b/frontend/src/metabase/home/homepage/components/HomeCaption/HomeCaption.styled.tsx
new file mode 100644
index 00000000000..7c4cfcb6100
--- /dev/null
+++ b/frontend/src/metabase/home/homepage/components/HomeCaption/HomeCaption.styled.tsx
@@ -0,0 +1,20 @@
+import styled from "@emotion/styled";
+import { color } from "metabase/lib/colors";
+import { breakpointMinExtraLarge } from "metabase/styled-components/theme";
+
+export interface CaptionProps {
+  primary?: boolean;
+}
+
+export const CaptionRoot = styled.div<CaptionProps>`
+  display: flex;
+  align-items: center;
+  color: ${props =>
+    props.primary ? color("text-dark") : color("text-medium")};
+  font-weight: bold;
+  margin-bottom: 1.5rem;
+
+  ${breakpointMinExtraLarge} {
+    margin-bottom: 2rem;
+  }
+`;
diff --git a/frontend/src/metabase/home/homepage/components/HomeCaption/HomeCaption.tsx b/frontend/src/metabase/home/homepage/components/HomeCaption/HomeCaption.tsx
new file mode 100644
index 00000000000..ca922b65503
--- /dev/null
+++ b/frontend/src/metabase/home/homepage/components/HomeCaption/HomeCaption.tsx
@@ -0,0 +1,13 @@
+import React, { ReactNode } from "react";
+import { CaptionRoot } from "./HomeCaption.styled";
+
+export interface HomeCaptionProps {
+  primary?: boolean;
+  children?: ReactNode;
+}
+
+const HomeCaption = ({ primary, children }: HomeCaptionProps): JSX.Element => {
+  return <CaptionRoot primary={primary}>{children}</CaptionRoot>;
+};
+
+export default HomeCaption;
diff --git a/frontend/src/metabase/home/homepage/components/HomeCaption/HomeCaption.unit.spec.tsx b/frontend/src/metabase/home/homepage/components/HomeCaption/HomeCaption.unit.spec.tsx
new file mode 100644
index 00000000000..dbe6ccad15f
--- /dev/null
+++ b/frontend/src/metabase/home/homepage/components/HomeCaption/HomeCaption.unit.spec.tsx
@@ -0,0 +1,11 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import HomeCaption from "./HomeCaption";
+
+describe("HomeCaption", () => {
+  it("should render correctly", () => {
+    render(<HomeCaption>Title</HomeCaption>);
+
+    expect(screen.getByText("Title")).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/home/homepage/components/HomeCaption/index.ts b/frontend/src/metabase/home/homepage/components/HomeCaption/index.ts
new file mode 100644
index 00000000000..2c0370af339
--- /dev/null
+++ b/frontend/src/metabase/home/homepage/components/HomeCaption/index.ts
@@ -0,0 +1 @@
+export { default } from "./HomeCaption";
diff --git a/frontend/src/metabase/home/homepage/components/HomeCard/HomeCard.styled.tsx b/frontend/src/metabase/home/homepage/components/HomeCard/HomeCard.styled.tsx
index b9a6e4c1c14..ee0b1b61b3e 100644
--- a/frontend/src/metabase/home/homepage/components/HomeCard/HomeCard.styled.tsx
+++ b/frontend/src/metabase/home/homepage/components/HomeCard/HomeCard.styled.tsx
@@ -1,7 +1,10 @@
 import styled from "@emotion/styled";
 import { alpha, color } from "metabase/lib/colors";
 import Link from "metabase/core/components/Link";
-import { breakpointMinSmall } from "metabase/styled-components/theme";
+import {
+  breakpointMinLarge,
+  breakpointMinSmall,
+} from "metabase/styled-components/theme";
 
 export const CardRoot = styled(Link)`
   display: flex;
@@ -17,6 +20,10 @@ export const CardRoot = styled(Link)`
     max-width: 50%;
   }
 
+  ${breakpointMinLarge} {
+    padding: 1.5rem;
+  }
+
   &:hover {
     box-shadow: 0 10px 22px ${alpha("shadow", 0.09)};
   }
diff --git a/frontend/src/metabase/home/homepage/components/HomeCard/HomeCard.unit.spec.tsx b/frontend/src/metabase/home/homepage/components/HomeCard/HomeCard.unit.spec.tsx
new file mode 100644
index 00000000000..a4bfe24e179
--- /dev/null
+++ b/frontend/src/metabase/home/homepage/components/HomeCard/HomeCard.unit.spec.tsx
@@ -0,0 +1,11 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import HomeCard from "./HomeCard";
+
+describe("HomeCard", () => {
+  it("should render correctly", () => {
+    render(<HomeCard>A look at table</HomeCard>);
+
+    expect(screen.getByText("A look at table")).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/home/homepage/components/HomeContent/HomeContent.tsx b/frontend/src/metabase/home/homepage/components/HomeContent/HomeContent.tsx
index 733f1283720..41139c5e97c 100644
--- a/frontend/src/metabase/home/homepage/components/HomeContent/HomeContent.tsx
+++ b/frontend/src/metabase/home/homepage/components/HomeContent/HomeContent.tsx
@@ -2,7 +2,7 @@ import React from "react";
 import moment from "moment";
 import { parseTimestamp } from "metabase/lib/time";
 import { isSyncCompleted } from "metabase/lib/syncing";
-import { Database, RecentView, User } from "metabase-types/api";
+import { Database, RecentItem, User } from "metabase-types/api";
 import HomePopularSection from "../../containers/HomePopularSection";
 import HomeRecentSection from "../../containers/HomeRecentSection";
 import HomeXraySection from "../../containers/HomeXraySection";
@@ -10,7 +10,7 @@ import HomeXraySection from "../../containers/HomeXraySection";
 export interface HomeContentProps {
   user: User;
   databases: Database[];
-  recentViews: RecentView[];
+  recentItems: RecentItem[];
 }
 
 const HomeContent = (props: HomeContentProps): JSX.Element | null => {
@@ -29,16 +29,16 @@ const HomeContent = (props: HomeContentProps): JSX.Element | null => {
   return null;
 };
 
-const isPopularSection = ({ user, recentViews }: HomeContentProps) => {
+const isPopularSection = ({ user, recentItems }: HomeContentProps) => {
   return (
     !user.is_installer &&
     user.has_question_and_dashboard &&
-    (isWithinWeek(user.date_joined) || !recentViews.length)
+    (isWithinWeek(user.first_login) || !recentItems.length)
   );
 };
 
-const isRecentSection = ({ recentViews }: HomeContentProps) => {
-  return recentViews.length > 0;
+const isRecentSection = ({ recentItems }: HomeContentProps) => {
+  return recentItems.length > 0;
 };
 
 const isXraySection = ({ databases }: HomeContentProps) => {
@@ -47,10 +47,8 @@ const isXraySection = ({ databases }: HomeContentProps) => {
 
 const isWithinWeek = (timestamp: string) => {
   const date = parseTimestamp(timestamp);
-  const today = moment();
-  const weekAgo = today.clone().subtract(1, "week");
-
-  return date.isBetween(weekAgo, today);
+  const weekAgo = moment().subtract(1, "week");
+  return date.isAfter(weekAgo);
 };
 
 export default HomeContent;
diff --git a/frontend/src/metabase/home/homepage/components/HomeContent/HomeContent.unit.spec.tsx b/frontend/src/metabase/home/homepage/components/HomeContent/HomeContent.unit.spec.tsx
new file mode 100644
index 00000000000..4856f0426c7
--- /dev/null
+++ b/frontend/src/metabase/home/homepage/components/HomeContent/HomeContent.unit.spec.tsx
@@ -0,0 +1,115 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import {
+  createMockDatabase,
+  createMockRecentItem,
+  createMockUser,
+} from "metabase-types/api/mocks";
+import HomeContent, { HomeContentProps } from "./HomeContent";
+
+const PopularSectionMock = () => <div>PopularSection</div>;
+jest.mock("../../containers/HomePopularSection", () => PopularSectionMock);
+
+const RecentSectionMock = () => <div>RecentSection</div>;
+jest.mock("../../containers/HomeRecentSection", () => RecentSectionMock);
+
+const XraySectionMock = () => <div>XraySection</div>;
+jest.mock("../../containers/HomeXraySection", () => XraySectionMock);
+
+describe("HomeContent", () => {
+  beforeEach(() => {
+    jest.useFakeTimers();
+    jest.setSystemTime(new Date(2020, 0, 10));
+  });
+
+  afterEach(() => {
+    jest.useRealTimers();
+  });
+
+  it("should render popular items for a new user", () => {
+    const props = getProps({
+      user: createMockUser({
+        is_installer: false,
+        has_question_and_dashboard: true,
+        first_login: "2020-01-05T00:00:00Z",
+      }),
+      databases: [createMockDatabase()],
+      recentItems: [createMockRecentItem()],
+    });
+
+    render(<HomeContent {...props} />);
+
+    expect(screen.getByText("PopularSection")).toBeInTheDocument();
+  });
+
+  it("should render popular items for a user without recent items", () => {
+    const props = getProps({
+      user: createMockUser({
+        is_installer: false,
+        has_question_and_dashboard: true,
+        first_login: "2020-01-05T00:00:00Z",
+      }),
+      databases: [createMockDatabase()],
+      recentItems: [],
+    });
+
+    render(<HomeContent {...props} />);
+
+    expect(screen.getByText("PopularSection")).toBeInTheDocument();
+  });
+
+  it("should render recent items for an existing user", () => {
+    const props = getProps({
+      user: createMockUser({
+        is_installer: false,
+        has_question_and_dashboard: true,
+        first_login: "2020-01-01T00:00:00Z",
+      }),
+      databases: [createMockDatabase()],
+      recentItems: [createMockRecentItem()],
+    });
+
+    render(<HomeContent {...props} />);
+
+    expect(screen.getByText("RecentSection")).toBeInTheDocument();
+  });
+
+  it("should render x-rays for an installer after the setup", () => {
+    const props = getProps({
+      user: createMockUser({
+        is_installer: true,
+        has_question_and_dashboard: false,
+        first_login: "2020-01-10T00:00:00Z",
+      }),
+      databases: [createMockDatabase()],
+      recentItems: [],
+    });
+
+    render(<HomeContent {...props} />);
+
+    expect(screen.getByText("XraySection")).toBeInTheDocument();
+  });
+
+  it("should render nothing if there are no databases", () => {
+    const props = getProps({
+      user: createMockUser({
+        is_installer: true,
+        has_question_and_dashboard: false,
+        first_login: "2020-01-10T00:00:00Z",
+      }),
+      databases: [],
+      recentItems: [],
+    });
+
+    render(<HomeContent {...props} />);
+
+    expect(screen.queryByText("XraySection")).not.toBeInTheDocument();
+  });
+});
+
+const getProps = (opts?: Partial<HomeContentProps>): HomeContentProps => ({
+  user: createMockUser(),
+  databases: [],
+  recentItems: [],
+  ...opts,
+});
diff --git a/frontend/src/metabase/home/homepage/components/HomeGreeting/HomeGreeting.styled.tsx b/frontend/src/metabase/home/homepage/components/HomeGreeting/HomeGreeting.styled.tsx
index 257065f86e4..6ee18643010 100644
--- a/frontend/src/metabase/home/homepage/components/HomeGreeting/HomeGreeting.styled.tsx
+++ b/frontend/src/metabase/home/homepage/components/HomeGreeting/HomeGreeting.styled.tsx
@@ -1,6 +1,7 @@
 import styled from "@emotion/styled";
 import { color } from "metabase/lib/colors";
 import MetabotLogo from "metabase/components/MetabotLogo";
+import { breakpointMinExtraLarge } from "metabase/styled-components/theme";
 
 export const GreetingRoot = styled.div`
   display: flex;
@@ -9,6 +10,10 @@ export const GreetingRoot = styled.div`
 
 export const GreetingLogo = styled(MetabotLogo)`
   height: 2rem;
+
+  ${breakpointMinExtraLarge} {
+    height: 2.5rem;
+  }
 `;
 
 export interface GreetingMessageProps {
@@ -21,4 +26,8 @@ export const GreetingMessage = styled.span<GreetingMessageProps>`
   font-weight: bold;
   line-height: 1.5rem;
   margin-left: ${props => props.showLogo && "0.5rem"};
+
+  ${breakpointMinExtraLarge} {
+    font-size: ${props => (props.showLogo ? "1.25rem" : "1.5rem")};
+  }
 `;
diff --git a/frontend/src/metabase/home/homepage/components/HomeGreeting/HomeGreeting.unit.spec.tsx b/frontend/src/metabase/home/homepage/components/HomeGreeting/HomeGreeting.unit.spec.tsx
new file mode 100644
index 00000000000..cc9c39e3ac3
--- /dev/null
+++ b/frontend/src/metabase/home/homepage/components/HomeGreeting/HomeGreeting.unit.spec.tsx
@@ -0,0 +1,36 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import { createMockUser } from "metabase-types/api/mocks";
+import HomeGreeting, { HomeGreetingProps } from "./HomeGreeting";
+
+describe("HomeGreeting", () => {
+  it("should render with logo", () => {
+    const props = getProps({
+      user: createMockUser({ first_name: "John" }),
+      showLogo: true,
+    });
+
+    render(<HomeGreeting {...props} />);
+
+    expect(screen.getByText(/John/)).toBeInTheDocument();
+    expect(screen.getByRole("img")).toBeInTheDocument();
+  });
+
+  it("should render without logo", () => {
+    const props = getProps({
+      user: createMockUser({ first_name: "John" }),
+      showLogo: false,
+    });
+
+    render(<HomeGreeting {...props} />);
+
+    expect(screen.getByText(/John/)).toBeInTheDocument();
+    expect(screen.queryByRole("img")).not.toBeInTheDocument();
+  });
+});
+
+const getProps = (opts?: Partial<HomeGreetingProps>): HomeGreetingProps => ({
+  user: createMockUser(),
+  showLogo: false,
+  ...opts,
+});
diff --git a/frontend/src/metabase/home/homepage/components/HomeHelpCard/HomeHelpCard.styled.tsx b/frontend/src/metabase/home/homepage/components/HomeHelpCard/HomeHelpCard.styled.tsx
index f77d1bd084d..0e3f63983fd 100644
--- a/frontend/src/metabase/home/homepage/components/HomeHelpCard/HomeHelpCard.styled.tsx
+++ b/frontend/src/metabase/home/homepage/components/HomeHelpCard/HomeHelpCard.styled.tsx
@@ -2,6 +2,7 @@ import styled from "@emotion/styled";
 import { color } from "metabase/lib/colors";
 import Icon from "metabase/components/Icon";
 import ExternalLink from "metabase/core/components/ExternalLink";
+import { breakpointMinLarge } from "metabase/styled-components/theme";
 
 export const CardRoot = styled(ExternalLink)`
   display: flex;
@@ -9,6 +10,10 @@ export const CardRoot = styled(ExternalLink)`
   padding: 1rem;
   border: 1px solid ${color("focus")};
   border-radius: 0.5rem;
+
+  ${breakpointMinLarge} {
+    padding: 1.5rem;
+  }
 `;
 
 export const CardIcon = styled(Icon)`
diff --git a/frontend/src/metabase/home/homepage/components/HomeHelpCard/HomeHelpCard.unit.spec.tsx b/frontend/src/metabase/home/homepage/components/HomeHelpCard/HomeHelpCard.unit.spec.tsx
new file mode 100644
index 00000000000..8b0bccaebf9
--- /dev/null
+++ b/frontend/src/metabase/home/homepage/components/HomeHelpCard/HomeHelpCard.unit.spec.tsx
@@ -0,0 +1,11 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import HomeHelpCard from "./HomeHelpCard";
+
+describe("HomeHelpCard", () => {
+  it("should render correctly", () => {
+    render(<HomeHelpCard />);
+
+    expect(screen.getByText("Metabase tips")).toBeInTheDocument();
+  });
+});
diff --git a/frontend/src/metabase/home/homepage/components/HomeLayout/HomeLayout.styled.tsx b/frontend/src/metabase/home/homepage/components/HomeLayout/HomeLayout.styled.tsx
index 30f0a7f6f27..7fda46edab6 100644
--- a/frontend/src/metabase/home/homepage/components/HomeLayout/HomeLayout.styled.tsx
+++ b/frontend/src/metabase/home/homepage/components/HomeLayout/HomeLayout.styled.tsx
@@ -1,6 +1,11 @@
 import styled from "@emotion/styled";
 import { css } from "@emotion/react";
 import { alpha, color } from "metabase/lib/colors";
+import {
+  breakpointMinExtraLarge,
+  breakpointMinLarge,
+  breakpointMinMedium,
+} from "metabase/styled-components/theme";
 
 export interface LayoutProps {
   showScene?: boolean;
@@ -24,10 +29,30 @@ const gradientStyles = css`
 
 export const LayoutRoot = styled.div<LayoutProps>`
   min-height: 100%;
-  padding: 4rem 7rem;
+  padding: 1rem;
   ${props => (props.showScene ? sceneStyles : gradientStyles)};
+
+  ${breakpointMinMedium} {
+    padding: 3rem 4rem;
+  }
+
+  ${breakpointMinLarge} {
+    padding: 4rem 7rem 2rem;
+  }
+
+  ${breakpointMinExtraLarge} {
+    padding: 10rem 15rem 4rem;
+  }
 `;
 
 export const LayoutBody = styled.div`
-  margin-top: 6rem;
+  margin-top: 2.5rem;
+
+  ${breakpointMinMedium} {
+    margin-top: 4rem;
+  }
+
+  ${breakpointMinLarge} {
+    margin-top: 6rem;
+  }
 `;
diff --git a/frontend/src/metabase/home/homepage/components/HomeLayout/HomeLayout.unit.spec.tsx b/frontend/src/metabase/home/homepage/components/HomeLayout/HomeLayout.unit.spec.tsx
new file mode 100644
index 00000000000..40db6cd646f
--- /dev/null
+++ b/frontend/src/metabase/home/homepage/components/HomeLayout/HomeLayout.unit.spec.tsx
@@ -0,0 +1,21 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import HomeLayout, { HomeLayoutProps } from "./HomeLayout";
+
+const HomeGreetingMock = () => <div>Hey there</div>;
+jest.mock("../../containers/HomeGreeting", () => HomeGreetingMock);
+
+describe("HomeLayout", () => {
+  it("should render correctly", () => {
+    const props = getProps();
+
+    render(<HomeLayout {...props} />);
+
+    expect(screen.getByText("Hey there")).toBeInTheDocument();
+  });
+});
+
+const getProps = (opts?: Partial<HomeLayoutProps>): HomeLayoutProps => ({
+  showScene: false,
+  ...opts,
+});
diff --git a/frontend/src/metabase/home/homepage/components/HomeModelCard/HomeModelCard.unit.spec.tsx b/frontend/src/metabase/home/homepage/components/HomeModelCard/HomeModelCard.unit.spec.tsx
new file mode 100644
index 00000000000..e9f485f5754
--- /dev/null
+++ b/frontend/src/metabase/home/homepage/components/HomeModelCard/HomeModelCard.unit.spec.tsx
@@ -0,0 +1,24 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import HomeModelCard, { HomeModelCardProps } from "./HomeModelCard";
+
+describe("HomeModelCard", () => {
+  it("should render correctly", () => {
+    const props = getProps({
+      title: "Orders",
+      icon: { name: "table" },
+    });
+
+    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/homepage/components/HomePage/HomePage.unit.spec.tsx b/frontend/src/metabase/home/homepage/components/HomePage/HomePage.unit.spec.tsx
new file mode 100644
index 00000000000..c934721207c
--- /dev/null
+++ b/frontend/src/metabase/home/homepage/components/HomePage/HomePage.unit.spec.tsx
@@ -0,0 +1,42 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import * as dom from "metabase/lib/dom";
+import HomePage from "./HomePage";
+
+jest.mock("metabase/lib/dom");
+
+const LayoutMock = () => <div />;
+jest.mock("../../containers/HomeLayout", () => LayoutMock);
+
+const ContentMock = () => <div />;
+jest.mock("../../containers/HomeContent", () => ContentMock);
+
+describe("HomePage", () => {
+  let isSmallScreenSpy: jest.SpyInstance;
+
+  beforeEach(() => {
+    isSmallScreenSpy = jest.spyOn(dom, "isSmallScreen");
+  });
+
+  afterEach(() => {
+    isSmallScreenSpy.mockRestore();
+  });
+
+  it("should open the navbar on a regular screen", () => {
+    const onOpenNavbar = jest.fn();
+    isSmallScreenSpy.mockReturnValue(false);
+
+    render(<HomePage onOpenNavbar={onOpenNavbar} />);
+
+    expect(onOpenNavbar).toHaveBeenCalled();
+  });
+
+  it("should not open the navbar on a small screen", () => {
+    const onOpenNavbar = jest.fn();
+    isSmallScreenSpy.mockReturnValue(true);
+
+    render(<HomePage onOpenNavbar={onOpenNavbar} />);
+
+    expect(onOpenNavbar).not.toHaveBeenCalled();
+  });
+});
diff --git a/frontend/src/metabase/home/homepage/components/HomePopularSection/HomePopularSection.styled.tsx b/frontend/src/metabase/home/homepage/components/HomePopularSection/HomePopularSection.styled.tsx
index d37a1a3cc1e..282480366e3 100644
--- a/frontend/src/metabase/home/homepage/components/HomePopularSection/HomePopularSection.styled.tsx
+++ b/frontend/src/metabase/home/homepage/components/HomePopularSection/HomePopularSection.styled.tsx
@@ -1,16 +1,4 @@
 import styled from "@emotion/styled";
-import { color } from "metabase/lib/colors";
-import HomeCard from "metabase/home/homepage/components/HomeCard";
-import Icon from "metabase/components/Icon";
-import Ellipsified from "metabase/components/Ellipsified";
-
-export const SectionTitle = styled.div`
-  display: flex;
-  align-items: center;
-  color: ${color("text-medium")};
-  font-weight: bold;
-  margin-bottom: 1.5rem;
-`;
 
 export const SectionBody = styled.div`
   display: flex;
diff --git a/frontend/src/metabase/home/homepage/components/HomePopularSection/HomePopularSection.tsx b/frontend/src/metabase/home/homepage/components/HomePopularSection/HomePopularSection.tsx
index 27161a7a6f4..74ebd1cd1be 100644
--- a/frontend/src/metabase/home/homepage/components/HomePopularSection/HomePopularSection.tsx
+++ b/frontend/src/metabase/home/homepage/components/HomePopularSection/HomePopularSection.tsx
@@ -2,24 +2,25 @@ import React from "react";
 import { t } from "ttag";
 import _ from "underscore";
 import * as Urls from "metabase/lib/urls";
-import { getIcon, getName } from "metabase/entities/popular-views";
-import { PopularView } from "metabase-types/api";
+import { getIcon, getName } from "metabase/entities/popular-items";
+import { PopularItem } from "metabase-types/api";
+import HomeCaption from "../HomeCaption";
 import HomeHelpCard from "../HomeHelpCard";
 import HomeModelCard from "../HomeModelCard";
-import { SectionBody, SectionTitle } from "./HomePopularSection.styled";
+import { SectionBody } from "./HomePopularSection.styled";
 
 export interface HomePopularSectionProps {
-  popularViews: PopularView[];
+  popularItems: PopularItem[];
 }
 
 const HomePopularSection = ({
-  popularViews,
+  popularItems,
 }: HomePopularSectionProps): JSX.Element => {
   return (
     <div>
-      <SectionTitle>{getTitle(popularViews)}</SectionTitle>
+      <HomeCaption>{getTitle(popularItems)}</HomeCaption>
       <SectionBody>
-        {popularViews.map((item, index) => (
+        {popularItems.map((item, index) => (
           <HomeModelCard
             key={index}
             title={getName(item)}
@@ -33,11 +34,11 @@ const HomePopularSection = ({
   );
 };
 
-const getTitle = (popularViews: PopularView[]) => {
-  const models = _.uniq(popularViews.map(item => item.model));
+const getTitle = (popularItems: PopularItem[]) => {
+  const models = _.uniq(popularItems.map(item => item.model));
 
   if (models.length !== 1) {
-    return t`Here is some popular stuff`;
+    return t`Here are some popular items`;
   }
 
   switch (models[0]) {
@@ -50,7 +51,7 @@ const getTitle = (popularViews: PopularView[]) => {
     case "dashboard":
       return t`Here are some popular dashboards`;
     default:
-      return t`Here is some popular stuff`;
+      return t`Here are some popular items`;
   }
 };
 
diff --git a/frontend/src/metabase/home/homepage/components/HomePopularSection/HomePopularSection.unit.spec.tsx b/frontend/src/metabase/home/homepage/components/HomePopularSection/HomePopularSection.unit.spec.tsx
new file mode 100644
index 00000000000..d3a78db89e3
--- /dev/null
+++ b/frontend/src/metabase/home/homepage/components/HomePopularSection/HomePopularSection.unit.spec.tsx
@@ -0,0 +1,61 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import { createMockPopularItem } from "metabase-types/api/mocks";
+import HomePopularSection, {
+  HomePopularSectionProps,
+} from "./HomePopularSection";
+
+describe("HomePopularSection", () => {
+  it("should render a list of items of the same type", () => {
+    const props = getProps({
+      popularItems: [
+        createMockPopularItem({
+          model: "dashboard",
+          model_object: {
+            name: "Metrics",
+          },
+        }),
+        createMockPopularItem({
+          model: "dashboard",
+          model_object: {
+            name: "Revenue",
+          },
+        }),
+      ],
+    });
+
+    render(<HomePopularSection {...props} />);
+
+    expect(screen.getByText(/popular dashboards/)).toBeInTheDocument();
+  });
+
+  it("should render a list of items of different types", () => {
+    const props = getProps({
+      popularItems: [
+        createMockPopularItem({
+          model: "dashboard",
+          model_object: {
+            name: "Metrics",
+          },
+        }),
+        createMockPopularItem({
+          model: "card",
+          model_object: {
+            name: "Revenue",
+          },
+        }),
+      ],
+    });
+
+    render(<HomePopularSection {...props} />);
+
+    expect(screen.getByText(/popular items/)).toBeInTheDocument();
+  });
+});
+
+const getProps = (
+  opts?: Partial<HomePopularSectionProps>,
+): HomePopularSectionProps => ({
+  popularItems: [],
+  ...opts,
+});
diff --git a/frontend/src/metabase/home/homepage/components/HomeRecentSection/HomeRecentSection.styled.tsx b/frontend/src/metabase/home/homepage/components/HomeRecentSection/HomeRecentSection.styled.tsx
index e82e897ce87..282480366e3 100644
--- a/frontend/src/metabase/home/homepage/components/HomeRecentSection/HomeRecentSection.styled.tsx
+++ b/frontend/src/metabase/home/homepage/components/HomeRecentSection/HomeRecentSection.styled.tsx
@@ -1,13 +1,4 @@
 import styled from "@emotion/styled";
-import { color } from "metabase/lib/colors";
-
-export const SectionTitle = styled.div`
-  display: flex;
-  align-items: center;
-  color: ${color("text-medium")};
-  font-weight: bold;
-  margin-bottom: 1.5rem;
-`;
 
 export const SectionBody = styled.div`
   display: flex;
diff --git a/frontend/src/metabase/home/homepage/components/HomeRecentSection/HomeRecentSection.tsx b/frontend/src/metabase/home/homepage/components/HomeRecentSection/HomeRecentSection.tsx
index 7bce8f97ebf..8d62d0f5752 100644
--- a/frontend/src/metabase/home/homepage/components/HomeRecentSection/HomeRecentSection.tsx
+++ b/frontend/src/metabase/home/homepage/components/HomeRecentSection/HomeRecentSection.tsx
@@ -1,23 +1,24 @@
 import React from "react";
 import { t } from "ttag";
 import * as Urls from "metabase/lib/urls";
-import { getIcon, getName } from "metabase/entities/recent-views";
-import { RecentView } from "metabase-types/api";
+import { getIcon, getName } from "metabase/entities/recent-items";
+import { RecentItem } from "metabase-types/api";
+import HomeCaption from "../HomeCaption";
 import HomeModelCard from "../HomeModelCard";
-import { SectionBody, SectionTitle } from "./HomeRecentSection.styled";
+import { SectionBody } from "./HomeRecentSection.styled";
 
 export interface HomeRecentSectionProps {
-  recentViews: RecentView[];
+  recentItems: RecentItem[];
 }
 
 const HomeRecentSection = ({
-  recentViews,
+  recentItems,
 }: HomeRecentSectionProps): JSX.Element => {
   return (
     <div>
-      <SectionTitle>{t`Pick up where you left off`}</SectionTitle>
+      <HomeCaption>{t`Pick up where you left off`}</HomeCaption>
       <SectionBody>
-        {recentViews.map((item, index) => (
+        {recentItems.map((item, index) => (
           <HomeModelCard
             key={index}
             title={getName(item)}
diff --git a/frontend/src/metabase/home/homepage/components/HomeRecentSection/HomeRecentSection.unit.spec.tsx b/frontend/src/metabase/home/homepage/components/HomeRecentSection/HomeRecentSection.unit.spec.tsx
new file mode 100644
index 00000000000..d7822fac5da
--- /dev/null
+++ b/frontend/src/metabase/home/homepage/components/HomeRecentSection/HomeRecentSection.unit.spec.tsx
@@ -0,0 +1,31 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import HomeRecentSection, { HomeRecentSectionProps } from "./HomeRecentSection";
+import { createMockRecentItem } from "metabase-types/api/mocks";
+
+describe("HomeRecentSection", () => {
+  it("should render a list of recent items", () => {
+    const props = getProps({
+      recentItems: [
+        createMockRecentItem({
+          model: "table",
+          model_object: {
+            name: "Orders",
+          },
+        }),
+      ],
+    });
+
+    render(<HomeRecentSection {...props} />);
+
+    expect(screen.getByText("Pick up where you left off")).toBeInTheDocument();
+    expect(screen.getByText("Orders")).toBeInTheDocument();
+  });
+});
+
+const getProps = (
+  opts?: Partial<HomeRecentSectionProps>,
+): HomeRecentSectionProps => ({
+  recentItems: [],
+  ...opts,
+});
diff --git a/frontend/src/metabase/home/homepage/components/HomeXrayCard/HomeXrayCard.unit.spec.tsx b/frontend/src/metabase/home/homepage/components/HomeXrayCard/HomeXrayCard.unit.spec.tsx
new file mode 100644
index 00000000000..3f987a71d69
--- /dev/null
+++ b/frontend/src/metabase/home/homepage/components/HomeXrayCard/HomeXrayCard.unit.spec.tsx
@@ -0,0 +1,24 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import HomeXrayCard, { HomeXrayCardProps } from "./HomeXrayCard";
+
+describe("HomeXrayCard", () => {
+  it("should render correctly", () => {
+    const props = getProps({
+      title: "Orders",
+      message: "A look at",
+    });
+
+    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/homepage/components/HomeXraySection/HomeXraySection.styled.tsx b/frontend/src/metabase/home/homepage/components/HomeXraySection/HomeXraySection.styled.tsx
index 0b260cdf1aa..e849ea1a4a3 100644
--- a/frontend/src/metabase/home/homepage/components/HomeXraySection/HomeXraySection.styled.tsx
+++ b/frontend/src/metabase/home/homepage/components/HomeXraySection/HomeXraySection.styled.tsx
@@ -3,12 +3,10 @@ import { color } from "metabase/lib/colors";
 import Link from "metabase/core/components/Link";
 import Icon from "metabase/components/Icon";
 
-export const SectionTitle = styled.div`
+export const SectionBody = styled.div`
   display: flex;
-  align-items: center;
-  color: ${color("text-dark")};
-  font-weight: bold;
-  margin-bottom: 1.5rem;
+  gap: 1.5rem;
+  flex-wrap: wrap;
 `;
 
 export const DatabaseLink = styled(Link)`
@@ -17,20 +15,33 @@ export const DatabaseLink = styled(Link)`
   margin-left: 0.5rem;
 `;
 
-export const DatabaseIcon = styled(Icon)`
+export const DatabaseLinkIcon = styled(Icon)`
   color: ${color("focus")};
   width: 1rem;
   height: 1rem;
   margin-right: 0.25rem;
 `;
 
-export const DatabaseTitle = styled.span`
+export const DatabaseLinkText = styled.span`
   color: ${color("brand")};
   font-weight: bold;
 `;
 
-export const XrayList = styled.div`
+export const SchemaTrigger = styled.span`
   display: flex;
-  gap: 1.5rem;
-  flex-wrap: wrap;
+  align-items: center;
+  margin: 0 0.5rem;
+  cursor: pointer;
+`;
+
+export const SchemaTriggerIcon = styled(Icon)`
+  color: ${color("brand")};
+  width: 0.625rem;
+  height: 0.625rem;
+  margin-left: 0.25rem;
+`;
+
+export const SchemaTriggerText = styled.span`
+  color: ${color("brand")};
+  font-weight: bold;
 `;
diff --git a/frontend/src/metabase/home/homepage/components/HomeXraySection/HomeXraySection.tsx b/frontend/src/metabase/home/homepage/components/HomeXraySection/HomeXraySection.tsx
index 9b947700e31..be44c6a3d2c 100644
--- a/frontend/src/metabase/home/homepage/components/HomeXraySection/HomeXraySection.tsx
+++ b/frontend/src/metabase/home/homepage/components/HomeXraySection/HomeXraySection.tsx
@@ -1,48 +1,63 @@
-import React, { useMemo } from "react";
+import React, { ChangeEvent, useCallback, useMemo, useState } from "react";
 import _ from "underscore";
 import { t } from "ttag";
 import * as Urls from "metabase/lib/urls";
+import Select from "metabase/core/components/Select";
 import { Database, DatabaseCandidate } from "metabase-types/api";
+import HomeCaption from "../HomeCaption";
 import HomeXrayCard from "../HomeXrayCard";
 import {
-  DatabaseIcon,
+  DatabaseLinkIcon,
   DatabaseLink,
-  DatabaseTitle,
-  SectionTitle,
-  XrayList,
+  DatabaseLinkText,
+  SectionBody,
+  SchemaTrigger,
+  SchemaTriggerText,
+  SchemaTriggerIcon,
 } from "./HomeXraySection.styled";
 
-export interface XraySectionProps {
-  database?: Database;
-  databaseCandidates: DatabaseCandidate[];
+export interface HomeXraySectionProps {
+  database: Database;
+  candidates: DatabaseCandidate[];
 }
 
 const HomeXraySection = ({
   database,
-  databaseCandidates,
-}: XraySectionProps): JSX.Element => {
-  const isSample = !database || database.is_sample;
-  const tables = databaseCandidates.flatMap(d => d.tables);
-  const tableCount = tables.length;
+  candidates,
+}: HomeXraySectionProps): JSX.Element => {
+  const isSample = database.is_sample;
+  const schemas = candidates.map(d => d.schema);
+  const [schema, setSchema] = useState(schemas[0]);
+  const candidate = candidates.find(d => d.schema === schema);
+  const tableCount = candidate ? candidate.tables.length : 0;
   const tableMessages = useMemo(() => getMessages(tableCount), [tableCount]);
+  const canSelectSchema = schemas.length > 1;
 
   return (
     <div>
       {isSample ? (
-        <SectionTitle>
+        <HomeCaption primary>
           {t`Try out these sample x-rays to see what Metabase can do.`}
-        </SectionTitle>
+        </HomeCaption>
+      ) : canSelectSchema ? (
+        <HomeCaption primary>
+          {t`Here are some explorations of the`}
+          <SchemaSelect
+            schema={schema}
+            schemas={schemas}
+            onChange={setSchema}
+          />
+          {t`schema in`}
+          <DatabaseInfo database={database} />
+        </HomeCaption>
       ) : (
-        <SectionTitle>
+        <HomeCaption primary>
           {t`Here are some explorations of`}
-          <DatabaseLink to={Urls.browseDatabase(database)}>
-            <DatabaseIcon name="database" />
-            <DatabaseTitle>{database.name}</DatabaseTitle>
-          </DatabaseLink>
-        </SectionTitle>
+          <DatabaseInfo database={database} />
+        </HomeCaption>
       )}
-      <XrayList>
-        {tables.map((table, index) => (
+      <SectionBody>
+        {candidate?.tables.map((table, index) => (
           <HomeXrayCard
             key={table.url}
             title={table.title}
@@ -50,11 +65,57 @@ const HomeXraySection = ({
             message={tableMessages[index]}
           />
         ))}
-      </XrayList>
+      </SectionBody>
     </div>
   );
 };
 
+interface SchemaSelectProps {
+  schema: string;
+  schemas: string[];
+  onChange?: (schema: string) => void;
+}
+
+const SchemaSelect = ({ schema, schemas, onChange }: SchemaSelectProps) => {
+  const trigger = (
+    <SchemaTrigger>
+      <SchemaTriggerText>{schema}</SchemaTriggerText>
+      <SchemaTriggerIcon name="chevrondown" />
+    </SchemaTrigger>
+  );
+
+  const handleChange = useCallback(
+    (event: ChangeEvent<HTMLSelectElement>) => {
+      onChange?.(event.target.value);
+    },
+    [onChange],
+  );
+
+  return (
+    <Select
+      value={schema}
+      options={schemas}
+      optionNameFn={getSchemaOption}
+      optionValueFn={getSchemaOption}
+      onChange={handleChange}
+      triggerElement={trigger}
+    />
+  );
+};
+
+interface DatabaseInfoProps {
+  database: Database;
+}
+
+const DatabaseInfo = ({ database }: DatabaseInfoProps) => {
+  return (
+    <DatabaseLink to={Urls.browseDatabase(database)}>
+      <DatabaseLinkIcon name="database" />
+      <DatabaseLinkText>{database.name}</DatabaseLinkText>
+    </DatabaseLink>
+  );
+};
+
 const getMessages = (count: number) => {
   const options = [
     t`A look at`,
@@ -70,4 +131,8 @@ const getMessages = (count: number) => {
     .value();
 };
 
+const getSchemaOption = (schema: string) => {
+  return schema;
+};
+
 export default HomeXraySection;
diff --git a/frontend/src/metabase/home/homepage/components/HomeXraySection/HomeXraySection.unit.spec.tsx b/frontend/src/metabase/home/homepage/components/HomeXraySection/HomeXraySection.unit.spec.tsx
new file mode 100644
index 00000000000..e70ba019ced
--- /dev/null
+++ b/frontend/src/metabase/home/homepage/components/HomeXraySection/HomeXraySection.unit.spec.tsx
@@ -0,0 +1,66 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import {
+  createMockDatabase,
+  createMockDatabaseCandidate,
+  createMockTableCandidate,
+} from "metabase-types/api/mocks";
+import HomeXraySection, { HomeXraySectionProps } from "./HomeXraySection";
+
+describe("HomeXraySection", () => {
+  it("should show x-rays for a sample database", () => {
+    const props = getProps({
+      database: createMockDatabase({
+        is_sample: true,
+      }),
+      candidates: [
+        createMockDatabaseCandidate({
+          tables: [
+            createMockTableCandidate({ title: "Orders" }),
+            createMockTableCandidate({ title: "People" }),
+          ],
+        }),
+      ],
+    });
+
+    render(<HomeXraySection {...props} />);
+
+    expect(screen.getByText(/Try out these sample x-rays/)).toBeInTheDocument();
+    expect(screen.getByText("Orders")).toBeInTheDocument();
+    expect(screen.getByText("People")).toBeInTheDocument();
+  });
+
+  it("should show x-rays for a user database", () => {
+    const props = getProps({
+      database: createMockDatabase({
+        name: "H2",
+        is_sample: false,
+      }),
+      candidates: [
+        createMockDatabaseCandidate({
+          schema: "public",
+          tables: [createMockTableCandidate({ title: "Orders" })],
+        }),
+        createMockDatabaseCandidate({
+          schema: "internal",
+          tables: [createMockTableCandidate({ title: "People" })],
+        }),
+      ],
+    });
+
+    render(<HomeXraySection {...props} />);
+
+    expect(screen.getByText(/Here are some explorations/)).toBeInTheDocument();
+    expect(screen.getByText("H2")).toBeInTheDocument();
+    expect(screen.getByText("Orders")).toBeInTheDocument();
+    expect(screen.queryByText("People")).not.toBeInTheDocument();
+  });
+});
+
+const getProps = (
+  opts?: Partial<HomeXraySectionProps>,
+): HomeXraySectionProps => ({
+  database: createMockDatabase(),
+  candidates: [],
+  ...opts,
+});
diff --git a/frontend/src/metabase/home/homepage/containers/HomeContent/HomeContent.tsx b/frontend/src/metabase/home/homepage/containers/HomeContent/HomeContent.tsx
index 6039a94a9a2..3e8119cfb42 100644
--- a/frontend/src/metabase/home/homepage/containers/HomeContent/HomeContent.tsx
+++ b/frontend/src/metabase/home/homepage/containers/HomeContent/HomeContent.tsx
@@ -1,7 +1,7 @@
 import { connect } from "react-redux";
 import _ from "underscore";
 import Databases from "metabase/entities/databases";
-import RecentViews from "metabase/entities/recent-views";
+import RecentItems from "metabase/entities/recent-items";
 import { getUser } from "metabase/selectors/user";
 import { State } from "metabase-types/store";
 import HomeContent from "../../components/HomeContent";
@@ -12,6 +12,6 @@ const mapStateToProps = (state: State) => ({
 
 export default _.compose(
   Databases.loadList(),
-  RecentViews.loadList({ reload: true }),
+  RecentItems.loadList({ reload: true }),
   connect(mapStateToProps),
 )(HomeContent);
diff --git a/frontend/src/metabase/home/homepage/containers/HomePopularSection/HomePopularSection.tsx b/frontend/src/metabase/home/homepage/containers/HomePopularSection/HomePopularSection.tsx
index 1aca5deadd6..636ced3e075 100644
--- a/frontend/src/metabase/home/homepage/containers/HomePopularSection/HomePopularSection.tsx
+++ b/frontend/src/metabase/home/homepage/containers/HomePopularSection/HomePopularSection.tsx
@@ -1,4 +1,4 @@
-import PopularViews from "metabase/entities/popular-views";
+import PopularItems from "metabase/entities/popular-items";
 import HomePopularSection from "../../components/HomePopularSection";
 
-export default PopularViews.loadList({ reload: true })(HomePopularSection);
+export default PopularItems.loadList({ reload: true })(HomePopularSection);
diff --git a/frontend/src/metabase/home/homepage/containers/HomeRecentSection/HomeRecentSection.tsx b/frontend/src/metabase/home/homepage/containers/HomeRecentSection/HomeRecentSection.tsx
index b0ba5e69af9..d846b20bc7f 100644
--- a/frontend/src/metabase/home/homepage/containers/HomeRecentSection/HomeRecentSection.tsx
+++ b/frontend/src/metabase/home/homepage/containers/HomeRecentSection/HomeRecentSection.tsx
@@ -1,4 +1,4 @@
-import RecentViews from "metabase/entities/recent-views";
+import RecentItems from "metabase/entities/recent-items";
 import HomeRecentSection from "../../components/HomeRecentSection";
 
-export default RecentViews.loadList()(HomeRecentSection);
+export default RecentItems.loadList()(HomeRecentSection);
diff --git a/frontend/src/metabase/home/homepage/containers/HomeXraySection/HomeXraySection.tsx b/frontend/src/metabase/home/homepage/containers/HomeXraySection/HomeXraySection.tsx
index 6d8da58fbae..65d573d230b 100644
--- a/frontend/src/metabase/home/homepage/containers/HomeXraySection/HomeXraySection.tsx
+++ b/frontend/src/metabase/home/homepage/containers/HomeXraySection/HomeXraySection.tsx
@@ -29,5 +29,5 @@ const mapStateToProps = (state: State, props: XraySectionProps) => ({
 export default _.compose(
   Databases.loadList(),
   connect(mapStateToProps),
-  DatabaseCandidates.loadList({ query: getXrayQuery }),
+  DatabaseCandidates.loadList({ query: getXrayQuery, listName: "candidates" }),
 )(HomeXraySection);
diff --git a/frontend/src/metabase/nav/components/RecentsList.jsx b/frontend/src/metabase/nav/components/RecentsList.jsx
index 4ce1cfbf33b..34e83e89a3f 100644
--- a/frontend/src/metabase/nav/components/RecentsList.jsx
+++ b/frontend/src/metabase/nav/components/RecentsList.jsx
@@ -3,7 +3,7 @@ import PropTypes from "prop-types";
 import { t } from "ttag";
 import _ from "underscore";
 
-import RecentViews from "metabase/entities/recent-views";
+import RecentItems from "metabase/entities/recent-items";
 import Text from "metabase/components/type/Text";
 import * as Urls from "metabase/lib/urls";
 import { isSyncCompleted } from "metabase/lib/syncing";
@@ -138,7 +138,7 @@ const isItemLoading = ({ model, model_object }) => {
 };
 
 export default _.compose(
-  RecentViews.loadList({
+  RecentItems.loadList({
     wrapped: true,
     reload: true,
     loadingAndErrorWrapper: false,
diff --git a/frontend/src/metabase/schema.js b/frontend/src/metabase/schema.js
index c391b20d475..82e49b2bfa7 100644
--- a/frontend/src/metabase/schema.js
+++ b/frontend/src/metabase/schema.js
@@ -110,11 +110,11 @@ CollectionSchema.define({
   items: [ObjectUnionSchema],
 });
 
-export const RecentViewsSchema = new schema.Entity("recentViews", undefined, {
+export const RecentItemSchema = new schema.Entity("recentItems", undefined, {
   idAttribute: ({ model, model_id }) => `${model}:${model_id}`,
 });
 
-export const PopularViewsSchema = new schema.Entity("popularViews", undefined, {
+export const PopularItemSchema = new schema.Entity("popularItems", undefined, {
   idAttribute: ({ model, model_id }) => `${model}:${model_id}`,
 });
 
diff --git a/frontend/test/metabase/scenarios/onboarding/home/homepage.cy.spec.js b/frontend/test/metabase/scenarios/onboarding/home/homepage.cy.spec.js
index d0ffb096a06..97b414cca4b 100644
--- a/frontend/test/metabase/scenarios/onboarding/home/homepage.cy.spec.js
+++ b/frontend/test/metabase/scenarios/onboarding/home/homepage.cy.spec.js
@@ -1,7 +1,100 @@
-import { restore } from "__support__/e2e/cypress";
+import { restore, visitDashboard } from "__support__/e2e/cypress";
 
 describe("scenarios > home > homepage", () => {
   beforeEach(() => {
-    restore();
+    cy.intercept("GET", `/api/dashboard/**`).as("getDashboard");
+    cy.intercept("GET", "/api/automagic-*/table/**").as("getXrayDashboard");
+    cy.intercept("GET", "/api/automagic-*/database/**").as("getXrayCandidates");
+    cy.intercept("GET", "/api/activity/recent_views").as("getRecentItems");
+    cy.intercept("GET", "/api/activity/popular_items").as("getPopularItems");
+  });
+
+  it("should display x-rays for the sample database", () => {
+    restore("setup");
+    cy.signInAsAdmin();
+
+    cy.visit("/");
+    cy.wait("@getXrayCandidates");
+    cy.findByText("Try out these sample x-rays to see what Metabase can do.");
+    cy.findByText("Orders").click();
+
+    cy.wait("@getXrayDashboard");
+    cy.findByText("More X-rays");
+  });
+
+  it("should display x-rays for a user database", () => {
+    restore("setup");
+    cy.signInAsAdmin();
+    cy.addH2SampleDatabase({ name: "H2" });
+
+    cy.visit("/");
+    cy.wait("@getXrayCandidates");
+    cy.findByText("Here are some explorations of");
+    cy.findByText("H2");
+    cy.findByText("Orders").click();
+
+    cy.wait("@getXrayDashboard");
+    cy.findByText("More X-rays");
+  });
+
+  it("should allow switching between multiple schemas for x-rays", () => {
+    restore("setup");
+    cy.signInAsAdmin();
+    cy.addH2SampleDatabase({ name: "H2" });
+    cy.intercept("/api/automagic-*/database/**", getXrayCandidates());
+
+    cy.visit("/");
+    cy.findByText(/Here are some explorations of the/);
+    cy.findByText("public");
+    cy.findByText("H2");
+    cy.findByText("Orders");
+    cy.findByText("People").should("not.exist");
+
+    cy.findByText("public").click();
+    cy.findByText("private").click();
+    cy.findByText("People");
+    cy.findByText("Orders").should("not.exist");
+  });
+
+  it("should display recent items", () => {
+    restore("default");
+    cy.signInAsAdmin();
+
+    visitDashboard(1);
+    cy.findByText("Orders in a dashboard");
+
+    cy.visit("/");
+    cy.wait("@getRecentItems");
+    cy.findByText("Pick up where you left off");
+
+    cy.findByText("Orders in a dashboard").click();
+    cy.wait("@getDashboard");
+    cy.findByText("Orders");
+  });
+
+  it.skip("should display popular items for a new user", () => {
+    restore("default");
+    cy.signInAsNormalUser();
+
+    cy.visit("/");
+    cy.wait("@getPopularItems");
+    cy.findByText("Here are some popular items");
+
+    cy.findByText("Orders in a dashboard").click();
+    cy.wait("@getDashboard");
+    cy.findByText("Orders");
   });
 });
+
+const getXrayCandidates = () => [
+  {
+    id: "1/public",
+    schema: "public",
+    tables: [{ title: "Orders", url: "/auto/dashboard/table/1" }],
+  },
+  {
+    id: "1/private",
+    schema: "private",
+    tables: [{ title: "People", url: "/auto/dashboard/table/2" }],
+  },
+];
diff --git a/frontend/test/snapshot-creators/default.cy.snap.js b/frontend/test/snapshot-creators/default.cy.snap.js
index 46d3317acb0..0f890f900f0 100644
--- a/frontend/test/snapshot-creators/default.cy.snap.js
+++ b/frontend/test/snapshot-creators/default.cy.snap.js
@@ -30,6 +30,7 @@ describe("snapshots", () => {
       snapshot("blank");
       setup();
       updateSettings();
+      snapshot("setup");
       addUsersAndGroups();
       createCollections();
       withSampleDatabase(SAMPLE_DATABASE => {
-- 
GitLab