From ead905b105b88c9c524c034cc87eb16935e9c27f Mon Sep 17 00:00:00 2001
From: Nick Fitzpatrick <nick@metabase.com>
Date: Fri, 31 May 2024 11:56:05 -0300
Subject: [PATCH] React 18 (#41975)

* initial commit with most types sorted

* admin, binning, collections, custom-column and dashboard-cards suites

* Filters, Joins, Metrics, Models, Native and Native filters suites

* rest of e2e specs

* filters, actions, onboarding and custom column will hopfully be green?

* fixing static viz, brush filtering, login redirecting, and a few other tests

* types green

* linter green

* hopefully fixes filter tests

* I don't often cry, but when I do it's because of filter-types

* hopfully sorted the last test that's failing CI

* PR Feedback

* yarn prettier

* React 18 Upgrade - Unit Tests (#41577)

* fixes for most unit tests to work with react 18 + upgraded rtl to v15

* fixes all unit tests locally

* fixes from rebasing

* linter shames me

* fix flake in maps.cy.spec.js

---------

Co-authored-by: Sloan Sparger <sloansparger@users.noreply.github.com>
Co-authored-by: Sloan Sparger <sloansparger@gmail.com>
---
 e2e/support/helpers/e2e-action-helpers.js     |   4 +-
 e2e/support/helpers/e2e-bi-basics-helpers.js  |   2 +-
 .../helpers/e2e-command-palette-helpers.js    |   8 +-
 .../helpers/e2e-custom-column-helpers.js      |   9 +-
 e2e/support/helpers/e2e-dashboard-helpers.js  |  11 +-
 .../helpers/e2e-ui-elements-helpers.js        |  10 +
 .../actions/model-actions.cy.spec.js          |   2 +
 .../admin/settings/localization.cy.spec.js    |   5 +-
 .../binning/qb-explicit-joins.cy.spec.js      |  10 +-
 .../binning/qb-regular-table.cy.spec.js       |  10 +-
 e2e/test/scenarios/binning/sql.cy.spec.js     |  10 +-
 .../collections/instance-analytics.cy.spec.js |   5 +-
 .../custom-column/cc-help-text.cy.spec.js     |  13 +-
 .../cc-typing-suggestion.cy.spec.js           |   5 +-
 .../custom-column/custom-column.cy.spec.js    |  10 +-
 ...45-cc-numeric-missing-summarize.cy.spec.js |   3 +-
 .../dashboard-cards/click-behavior.cy.spec.js |   2 +
 .../dashboard-card-undo.cy.spec.js            |   4 +
 .../dashboard-drill.cy.spec.js                |  11 +-
 .../dashboard-sections.cy.spec.js             |   6 +-
 .../dashcard-replace-question.cy.spec.js      |   2 +-
 ...scalar-first-render-size-change.cy.spec.js |   4 +-
 .../visualization-options.cy.spec.js          |   2 +-
 .../filters/filter-reproductions.cy.spec.js   |   4 +-
 .../scenarios/filters/filter-types.cy.spec.js |   9 +-
 e2e/test/scenarios/filters/filter.cy.spec.js  |  36 +-
 .../filters/relative-datetime.cy.spec.js      |  15 +-
 ...ultiple-filters-with-same-value.cy.spec.js |  10 +-
 .../22730-table-column-time-filter.cy.spec.js |   7 +-
 .../24664-multiple-filters-editing.cy.spec.js |  13 +-
 ...mn-filters-not-working-after-cc.cy.spec.js |   9 +-
 .../25990-filter-nested-join.cy.spec.js       |   2 +-
 ...clude-always-shows-days-of-week.cy.spec.js |   4 +-
 ...ill-filter-on-aggregated-column.cy.spec.js |   8 +-
 ...long-column-name-search-results.cy.spec.js |   9 +-
 .../9339-clipboard-numeric-filter.cy.spec.js  |   4 +-
 e2e/test/scenarios/models/models.cy.spec.js   |   4 +-
 .../scenarios/models/reproductions.cy.spec.js |   4 +-
 .../scenarios/models/reproductions.cy.spec.ts |  14 +-
 .../helpers/e2e-field-filter-helpers.js       |  13 +-
 .../reproductions/31606.cy.spec.js            |   4 +-
 .../sql-field-filter-number.cy.spec.js        |   1 -
 .../sql-field-filter-string.cy.spec.js        |   2 -
 ...rs-too-eagerly-on-initial-input.cy.spec.js |   3 +-
 .../onboarding/command-palette.cy.spec.js     |   4 +-
 .../onboarding/reference/databases.cy.spec.js |  33 +-
 .../5276-remove-field-type.cy.spec.js         |   6 +-
 .../reproductions-1.cy.spec.js                |   4 +-
 .../reproductions-2.cy.spec.js                |   8 +-
 e2e/test/scenarios/question/nested.cy.spec.js |  21 +-
 e2e/test/scenarios/question/new.cy.spec.js    |   9 +-
 .../scenarios/question/notebook.cy.spec.js    |   4 +-
 e2e/test/scenarios/question/saved.cy.spec.js  |   4 +-
 .../scenarios/question/settings.cy.spec.js    |  15 +-
 .../question/summarization.cy.spec.js         |   1 -
 ...ative-query-export-column-order.cy.spec.js |  14 +-
 ...c-sharing-embed-button-behavior.cy.spec.js |   2 +-
 ...on-shows-original-question-name.cy.spec.js |   2 +-
 .../line_chart.cy.spec.js                     |   7 +-
 .../visualizations-charts/maps.cy.spec.js     |   7 +-
 .../pie_chart.cy.spec.js                      |   9 +-
 ...aps-null-location-wrong-tooltip.cy.spec.js |   6 +-
 .../column-shortcuts.cy.spec.ts               |   5 +-
 .../drillthroughs/chart_drill.cy.spec.js      |  10 +-
 .../column_extract_drill.cy.spec.js           |   4 +-
 .../drillthroughs/combine-column.cy.spec.js   |   7 +-
 .../drillthroughs/table_drills.cy.spec.js     |  20 +-
 .../object_detail.cy.spec.js                  |   9 +-
 .../pivot_tables.cy.spec.js                   |   4 +-
 .../table-column-settings.cy.spec.js          |  12 +-
 .../visualizations-tabular/table.cy.spec.js   |  17 +-
 .../ApplicationPermissionsPage.tsx            |   6 +-
 .../components/CacheSection/CacheSection.tsx  |   2 +-
 frontend/src/metabase-types/api/actions.ts    |   4 +
 frontend/src/metabase/ErrorBoundary.tsx       |   4 +-
 .../ActionCreatorModal/ActionCreatorModal.tsx |  18 +-
 .../filters/pickers/DatePicker/DatePicker.tsx |   2 +-
 .../pickers/DatePicker/DatePickerFooter.tsx   |   2 +-
 .../admin/datamodel/containers/SegmentApp.jsx |  33 +-
 .../DatabasesPermissionsPage.unit.spec.tsx    |   2 +-
 .../GroupsPermissionsPage.unit.spec.tsx       |   2 +-
 .../components/Email/SMTPConnectionForm.tsx   |   6 +-
 frontend/src/metabase/api/action.ts           |  18 +
 frontend/src/metabase/api/index.ts            |   1 +
 frontend/src/metabase/app.js                  |   7 +-
 frontend/src/metabase/auth/actions.ts         |  14 +-
 .../CollectionContent/CollectionContent.tsx   |  19 +-
 .../NestedItemPicker.styled.tsx               |   2 +-
 .../src/metabase/components/tree/Tree.tsx     |   4 +-
 .../src/metabase/components/tree/TreeNode.tsx |   2 +-
 .../src/metabase/components/tree/types.ts     |   2 +-
 .../AdHocQuestionLoader.unit.spec.js          |   2 +-
 .../TableClickBehaviorView.tsx                |  10 +-
 .../dashboard/components/DashboardGrid.tsx    |   2 +-
 frontend/src/metabase/hoc/Favicon.jsx         |   4 +-
 .../src/metabase/lib/formatting/value.tsx     |   2 +-
 .../src/metabase/lib/redux/utils.unit.spec.js |   2 +-
 .../ModelDetailPage/ModelDetailPage.tsx       |   2 +-
 .../MainNavbarContainer.tsx                   |   2 +-
 .../MainNavbarContainer/MainNavbarView.tsx    |   2 +-
 .../public/components/PublicError.tsx         |   2 +-
 .../DataSelector/DataSelector.styled.tsx      |   2 +-
 .../DataSelector/DataSelector.unit.spec.js    |   2 +-
 .../NotebookNativePreview.tsx                 |   2 +-
 .../NotebookContainer/NotebookContainer.tsx   |  56 ++--
 frontend/src/metabase/route-guards.tsx        |  12 +-
 .../SearchResult/SearchResult.styled.tsx      |   9 +-
 frontend/src/metabase/static-viz/index.js     |   2 +
 .../ObjectDetail/ObjectDetailHeader.tsx       |   2 +-
 .../TableInteractive/TableInteractive.jsx     |  83 ++---
 .../components/TableSimple/TableSimple.tsx    |   2 +-
 .../Visualization-themed.unit.spec.tsx        |   2 +-
 .../Visualization/Visualization.unit.spec.js  |   2 +-
 .../ChartSettingsListColumns.tsx              |   2 +-
 .../CartesianChart/CartesianChart.styled.tsx  |   1 -
 .../CartesianChart/CartesianChart.tsx         |   2 +-
 frontend/src/types/ban-evil-use-callback.d.ts |   9 -
 frontend/test/__support__/utils.ts            |   8 +
 package.json                                  |  31 +-
 yarn.lock                                     | 311 +++++++-----------
 120 files changed, 682 insertions(+), 605 deletions(-)
 create mode 100644 frontend/src/metabase/api/action.ts
 delete mode 100644 frontend/src/types/ban-evil-use-callback.d.ts

diff --git a/e2e/support/helpers/e2e-action-helpers.js b/e2e/support/helpers/e2e-action-helpers.js
index 751d6d408ae..7a929ef99c6 100644
--- a/e2e/support/helpers/e2e-action-helpers.js
+++ b/e2e/support/helpers/e2e-action-helpers.js
@@ -9,8 +9,10 @@ export function setActionsEnabledForDB(dbId, enabled = true) {
 }
 
 export function fillActionQuery(query) {
-  cy.get(".ace_content:visible").type(query, {
+  // Without this wait, content tends to drop from the beginning of the string. TODO: Fix
+  cy.get(".ace_content:visible").wait(500).type(query, {
     parseSpecialCharSequences: false,
+    delay: 50,
   });
 }
 /**
diff --git a/e2e/support/helpers/e2e-bi-basics-helpers.js b/e2e/support/helpers/e2e-bi-basics-helpers.js
index 7de46001da2..98e9a62fca8 100644
--- a/e2e/support/helpers/e2e-bi-basics-helpers.js
+++ b/e2e/support/helpers/e2e-bi-basics-helpers.js
@@ -129,7 +129,7 @@ function getIcon(actionType) {
 export function assertQueryBuilderRowCount(count) {
   const message =
     count === 1 ? "Showing 1 row" : `Showing ${count.toLocaleString()} rows`;
-  cy.findByTestId("question-row-count").contains(message);
+  cy.findByTestId("question-row-count").should("contain.text", message);
 }
 
 /**
diff --git a/e2e/support/helpers/e2e-command-palette-helpers.js b/e2e/support/helpers/e2e-command-palette-helpers.js
index 03343cb30e3..f08bc290463 100644
--- a/e2e/support/helpers/e2e-command-palette-helpers.js
+++ b/e2e/support/helpers/e2e-command-palette-helpers.js
@@ -3,10 +3,10 @@ export const openCommandPalette = () => cy.get("body").type("{ctrl+k}{cmd+k}");
 export const commandPaletteButton = () =>
   cy.findByTestId("app-bar").findByRole("button", { name: /Search/ });
 export const closeCommandPalette = () => cy.get("body").type("{esc}");
-export const pressPageUp = () => cy.get("body").type("{pageup}");
-export const pressPageDown = () => cy.get("body").type("{pagedown}");
-export const pressHome = () => cy.get("body").type("{home}");
-export const pressEnd = () => cy.get("body").type("{end}");
+export const pressPageUp = () => cy.realPress("{pageup}");
+export const pressPageDown = () => cy.realPress("{pagedown}");
+export const pressHome = () => cy.realPress("{home}");
+export const pressEnd = () => cy.realPress("{end}");
 export const commandPaletteInput = () =>
   cy.findByPlaceholderText("Search for anything or jump somewhere…");
 
diff --git a/e2e/support/helpers/e2e-custom-column-helpers.js b/e2e/support/helpers/e2e-custom-column-helpers.js
index 169af0a0132..73c152a561b 100644
--- a/e2e/support/helpers/e2e-custom-column-helpers.js
+++ b/e2e/support/helpers/e2e-custom-column-helpers.js
@@ -6,8 +6,9 @@ export function expressionEditorWidget() {
  * @param {Object} option
  * @param {string} option.formula
  * @param {string=} option.name
+ * @param {boolean} option.blur true by default. However, if you need to examine the popover in the test, it should be set to false so the popover is not dismissed
  */
-export function enterCustomColumnDetails({ formula, name }) {
+export function enterCustomColumnDetails({ formula, name, blur = true }) {
   cy.get(".ace_text-input")
     .first()
     .as("formula")
@@ -15,11 +16,15 @@ export function enterCustomColumnDetails({ formula, name }) {
     .focus()
     .clear()
     .type(formula);
+  if (blur) {
+    cy.get("@formula").blur();
+  }
 
   if (name) {
     cy.findByPlaceholderText("Something nice and descriptive")
       .clear()
-      .type(name);
+      .type(name)
+      .blur();
   }
 }
 
diff --git a/e2e/support/helpers/e2e-dashboard-helpers.js b/e2e/support/helpers/e2e-dashboard-helpers.js
index ff1478a0e00..43259f79cde 100644
--- a/e2e/support/helpers/e2e-dashboard-helpers.js
+++ b/e2e/support/helpers/e2e-dashboard-helpers.js
@@ -83,12 +83,14 @@ export function showDashboardCardActions(index = 0) {
  * @returns {Cypress.Chainable<JQuery<HTMLElement>>}
  */
 export function findDashCardAction(dashcardElement, labelText) {
-  return dashcardElement.realHover().findByLabelText(labelText);
+  return dashcardElement
+    .realHover({ scrollBehavior: "bottom" })
+    .findByLabelText(labelText);
 }
 
 export function removeDashboardCard(index = 0) {
   getDashboardCard(index)
-    .realHover({ scrollBehavior: "bottom" })
+    .realHover()
     .findByTestId("dashboardcard-actions-panel")
     .should("be.visible")
     .icon("close")
@@ -236,11 +238,14 @@ export function resizeDashboardCard({ card, x, y }) {
     const resizeHandle = cy.get(".react-resizable-handle");
     resizeHandle
       .trigger("mousedown", { button: 0 })
+      .wait(200)
       .trigger("mousemove", {
         clientX: x,
         clientY: y,
       })
-      .trigger("mouseup", { force: true });
+      .wait(200)
+      .trigger("mouseup", { force: true })
+      .wait(200);
   });
 }
 
diff --git a/e2e/support/helpers/e2e-ui-elements-helpers.js b/e2e/support/helpers/e2e-ui-elements-helpers.js
index 24f4944e79d..45be3da72a5 100644
--- a/e2e/support/helpers/e2e-ui-elements-helpers.js
+++ b/e2e/support/helpers/e2e-ui-elements-helpers.js
@@ -239,3 +239,13 @@ export const undoToastList = () => {
 export function dashboardCards() {
   return cy.get("[data-element-id=dashboard-cards-container]");
 }
+
+export function tableHeaderClick(headerString) {
+  cy.findByTestId("TableInteractive-root").within(() => {
+    cy.findByTextEnsureVisible(headerString).trigger("mousedown");
+  });
+
+  cy.findByTestId("TableInteractive-root").within(() => {
+    cy.findByTextEnsureVisible(headerString).trigger("mouseup");
+  });
+}
diff --git a/e2e/test/scenarios/actions/model-actions.cy.spec.js b/e2e/test/scenarios/actions/model-actions.cy.spec.js
index aeab2f59621..d9134aa853a 100644
--- a/e2e/test/scenarios/actions/model-actions.cy.spec.js
+++ b/e2e/test/scenarios/actions/model-actions.cy.spec.js
@@ -98,6 +98,7 @@ describe(
       cy.intercept("GET", "/api/table/*/query_metadata*").as("fetchMetadata");
       cy.intercept("GET", "/api/search?archived=true").as("getArchived");
       cy.intercept("GET", "/api/search?*").as("getSearchResults");
+      cy.intercept("GET", "/api/database?*").as("getDatabase");
     });
 
     it("should allow CRUD operations on model actions", () => {
@@ -180,6 +181,7 @@ describe(
       cy.findByTestId("app-bar").findByText("New").click();
       popover().findByText("Action").click();
 
+      cy.wait("@getDatabase");
       fillActionQuery(QUERY);
 
       cy.findByRole("dialog").within(() => {
diff --git a/e2e/test/scenarios/admin/settings/localization.cy.spec.js b/e2e/test/scenarios/admin/settings/localization.cy.spec.js
index cd4bd392856..84c812c4ab4 100644
--- a/e2e/test/scenarios/admin/settings/localization.cy.spec.js
+++ b/e2e/test/scenarios/admin/settings/localization.cy.spec.js
@@ -8,6 +8,7 @@ import {
   popover,
   undoToast,
   echartsContainer,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 const { ORDERS, ORDERS_ID } = SAMPLE_DATABASE;
@@ -208,9 +209,7 @@ describe("scenarios > admin > localization", () => {
     visitQuestion(ORDERS_QUESTION_ID);
 
     // create a date filter and set it to the 'On' view to see a specific date
-    cy.findByTestId("TableInteractive-root")
-      .findByTextEnsureVisible("Created At")
-      .click();
+    tableHeaderClick("Created At");
 
     popover().within(() => {
       cy.findByText("Filter by this column").click();
diff --git a/e2e/test/scenarios/binning/qb-explicit-joins.cy.spec.js b/e2e/test/scenarios/binning/qb-explicit-joins.cy.spec.js
index 7dce4ea42b3..e3df9c4c5a3 100644
--- a/e2e/test/scenarios/binning/qb-explicit-joins.cy.spec.js
+++ b/e2e/test/scenarios/binning/qb-explicit-joins.cy.spec.js
@@ -10,6 +10,7 @@ import {
   cartesianChartCircle,
   chartPathWithFillColor,
   entityPickerModalTab,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 const { ORDERS_ID, ORDERS, PEOPLE_ID, PEOPLE, PRODUCTS_ID, PRODUCTS } =
@@ -216,8 +217,7 @@ describe("scenarios > binning > from a saved QB question with explicit joins", (
     });
 
     it("should work for time series", () => {
-      // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-      cy.findByText("People → Birth Date").click();
+      tableHeaderClick("People → Birth Date");
       // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
       cy.findByText("Distribution").click();
 
@@ -252,8 +252,7 @@ describe("scenarios > binning > from a saved QB question with explicit joins", (
     });
 
     it("should work for number", () => {
-      // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-      cy.findByText("Products → Price").click();
+      tableHeaderClick("Products → Price");
       // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
       cy.findByText("Distribution").click();
 
@@ -272,8 +271,7 @@ describe("scenarios > binning > from a saved QB question with explicit joins", (
     });
 
     it("should work for longitude", () => {
-      // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-      cy.findByText("People → Longitude").click();
+      tableHeaderClick("People → Longitude");
       // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
       cy.findByText("Distribution").click();
 
diff --git a/e2e/test/scenarios/binning/qb-regular-table.cy.spec.js b/e2e/test/scenarios/binning/qb-regular-table.cy.spec.js
index ad474772b96..76a36ae52ea 100644
--- a/e2e/test/scenarios/binning/qb-regular-table.cy.spec.js
+++ b/e2e/test/scenarios/binning/qb-regular-table.cy.spec.js
@@ -7,6 +7,7 @@ import {
   summarize,
   chartPathWithFillColor,
   cartesianChartCircle,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 const { ORDERS_ID, PEOPLE_ID } = SAMPLE_DATABASE;
@@ -117,8 +118,7 @@ describe("scenarios > binning > binning options", () => {
   context("via column popover", () => {
     it("should work for number", () => {
       openTable({ table: ORDERS_ID });
-      // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-      cy.findByText("Total").click();
+      tableHeaderClick("Total");
       // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
       cy.findByText("Distribution").click();
 
@@ -131,8 +131,7 @@ describe("scenarios > binning > binning options", () => {
 
     it("should work for time series", () => {
       openTable({ table: ORDERS_ID });
-      // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-      cy.findByText("Created At").click();
+      tableHeaderClick("Created At");
       // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
       cy.findByText("Distribution").click();
 
@@ -145,8 +144,7 @@ describe("scenarios > binning > binning options", () => {
 
     it("should work for longitude/latitude", () => {
       openTable({ table: PEOPLE_ID });
-      // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-      cy.findByText("Longitude").click();
+      tableHeaderClick("Longitude");
       // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
       cy.findByText("Distribution").click();
 
diff --git a/e2e/test/scenarios/binning/sql.cy.spec.js b/e2e/test/scenarios/binning/sql.cy.spec.js
index ba1da9c10e5..b51431313cf 100644
--- a/e2e/test/scenarios/binning/sql.cy.spec.js
+++ b/e2e/test/scenarios/binning/sql.cy.spec.js
@@ -10,6 +10,7 @@ import {
   cartesianChartCircle,
   chartPathWithFillColor,
   echartsContainer,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 const questionDetails = {
@@ -187,8 +188,7 @@ describe("scenarios > binning > from a saved sql question", () => {
     });
 
     it("should work for time series", () => {
-      // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-      cy.findByText("CREATED_AT").click();
+      tableHeaderClick("CREATED_AT");
       // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
       cy.findByText("Distribution").click();
 
@@ -209,8 +209,7 @@ describe("scenarios > binning > from a saved sql question", () => {
     });
 
     it("should work for number", () => {
-      // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-      cy.findByText("TOTAL").click();
+      tableHeaderClick("TOTAL");
       // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
       cy.findByText("Distribution").click();
 
@@ -221,8 +220,7 @@ describe("scenarios > binning > from a saved sql question", () => {
     });
 
     it("should work for longitude", () => {
-      // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-      cy.findByText("LONGITUDE").click();
+      tableHeaderClick("LONGITUDE");
       // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
       cy.findByText("Distribution").click();
 
diff --git a/e2e/test/scenarios/collections/instance-analytics.cy.spec.js b/e2e/test/scenarios/collections/instance-analytics.cy.spec.js
index 5e560c5187a..64b02fedc38 100644
--- a/e2e/test/scenarios/collections/instance-analytics.cy.spec.js
+++ b/e2e/test/scenarios/collections/instance-analytics.cy.spec.js
@@ -12,6 +12,7 @@ import {
   visitModel,
   visitQuestion,
   describeOSS,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 const ANALYTICS_COLLECTION_NAME = "Metabase analytics";
@@ -57,9 +58,7 @@ describeEE("scenarios > Metabase Analytics Collection (AuditV2) ", () => {
           visitModel(id);
         });
 
-        cy.findByTestId("TableInteractive-root").within(() => {
-          cy.findByText("Last Name").click();
-        });
+        tableHeaderClick("Last Name");
 
         popover().findByText("Filter by this column").click();
         cy.wait("@fieldValues");
diff --git a/e2e/test/scenarios/custom-column/cc-help-text.cy.spec.js b/e2e/test/scenarios/custom-column/cc-help-text.cy.spec.js
index 50c54f5e710..e89d7a75c25 100644
--- a/e2e/test/scenarios/custom-column/cc-help-text.cy.spec.js
+++ b/e2e/test/scenarios/custom-column/cc-help-text.cy.spec.js
@@ -15,25 +15,28 @@ describe("scenarios > question > custom column > help text", () => {
   });
 
   it("should appear while inside a function", () => {
-    enterCustomColumnDetails({ formula: "Lower(" });
+    enterCustomColumnDetails({ formula: "Lower(", blur: false });
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.contains("lower(text)");
   });
 
   it("should appear after a field reference", () => {
-    enterCustomColumnDetails({ formula: "Lower([Category]" });
+    enterCustomColumnDetails({ formula: "Lower([Category]", blur: false });
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.contains("lower(text)");
   });
 
   it("should not appear while outside a function", () => {
-    enterCustomColumnDetails({ formula: "Lower([Category])" });
+    enterCustomColumnDetails({ formula: "Lower([Category])", blur: false });
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.findByText("lower(text)").should("not.exist");
   });
 
   it("should not appear when formula field is not in focus (metabase#15891)", () => {
-    enterCustomColumnDetails({ formula: "rou{enter}1.5){leftArrow}" });
+    enterCustomColumnDetails({
+      formula: "rou{enter}1.5){leftArrow}",
+      blur: false,
+    });
 
     cy.findByTestId("expression-helper-popover").findByText(
       "round([Temperature])",
@@ -56,7 +59,7 @@ describe("scenarios > question > custom column > help text", () => {
   });
 
   it("should not disappear when clicked on (metabase#17548)", () => {
-    enterCustomColumnDetails({ formula: "rou{enter}" });
+    enterCustomColumnDetails({ formula: "rou{enter}", blur: false });
 
     // Shouldn't hide on click
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
diff --git a/e2e/test/scenarios/custom-column/cc-typing-suggestion.cy.spec.js b/e2e/test/scenarios/custom-column/cc-typing-suggestion.cy.spec.js
index 9bf8bb73ded..e23937551c1 100644
--- a/e2e/test/scenarios/custom-column/cc-typing-suggestion.cy.spec.js
+++ b/e2e/test/scenarios/custom-column/cc-typing-suggestion.cy.spec.js
@@ -24,6 +24,7 @@ describe("scenarios > question > custom column > typing suggestion", () => {
     addCustomColumn();
     enterCustomColumnDetails({
       formula: "[Rating]{leftarrow}{leftarrow}{leftarrow}",
+      blur: false,
     });
 
     // accept the only suggested item, i.e. "[Rating]"
@@ -37,7 +38,7 @@ describe("scenarios > question > custom column > typing suggestion", () => {
 
   it("should correctly accept the chosen function suggestion", () => {
     addCustomColumn();
-    enterCustomColumnDetails({ formula: "LTRIM([Title])" });
+    enterCustomColumnDetails({ formula: "LTRIM([Title])", blur: false });
 
     // Place the cursor between "is" and "empty"
     cy.get("@formula").type("{leftarrow}".repeat(13));
@@ -59,7 +60,7 @@ describe("scenarios > question > custom column > typing suggestion", () => {
 
   it("should show expression function helper if a proper function is typed", () => {
     addCustomColumn();
-    enterCustomColumnDetails({ formula: "lower(" });
+    enterCustomColumnDetails({ formula: "lower(", blur: false });
 
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.contains("lower(text)");
diff --git a/e2e/test/scenarios/custom-column/custom-column.cy.spec.js b/e2e/test/scenarios/custom-column/custom-column.cy.spec.js
index e67be6a05e1..9c5202c7d25 100644
--- a/e2e/test/scenarios/custom-column/custom-column.cy.spec.js
+++ b/e2e/test/scenarios/custom-column/custom-column.cy.spec.js
@@ -17,6 +17,7 @@ import {
   restore,
   startNewQuestion,
   summarize,
+  tableHeaderClick,
   visitQuestionAdhoc,
   visualize,
 } from "e2e/support/helpers";
@@ -600,8 +601,7 @@ describe("scenarios > question > custom column", () => {
       },
     });
 
-    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.findByText("CustomDate").click();
+    tableHeaderClick("CustomDate");
 
     popover().within(() => {
       cy.findByText("Filter by this column").click();
@@ -689,7 +689,7 @@ describe("scenarios > question > custom column", () => {
     openOrdersTable({ mode: "notebook" });
     cy.icon("add_data").click();
 
-    enterCustomColumnDetails({ formula: "[" });
+    enterCustomColumnDetails({ formula: "[C", blur: false });
 
     // Suggestion popover shows up and this select the first one ([Created At])
     cy.realPress("Tab");
@@ -697,6 +697,10 @@ describe("scenarios > question > custom column", () => {
     // Focus remains on the expression editor
     cy.focused().should("have.attr", "class").and("eq", "ace_text-input");
 
+    // This really shouldn't be needed, but without interacting with the field, we can't tab away from it.
+    // TODO: Fix
+    cy.get(".ace_text-input").first().type(" ");
+
     // Tab to focus on the name box
     cy.realPress("Tab");
     cy.focused().should("have.attr", "value").and("eq", "");
diff --git a/e2e/test/scenarios/custom-column/reproductions/27745-cc-numeric-missing-summarize.cy.spec.js b/e2e/test/scenarios/custom-column/reproductions/27745-cc-numeric-missing-summarize.cy.spec.js
index 48138223eba..6cf04002f1d 100644
--- a/e2e/test/scenarios/custom-column/reproductions/27745-cc-numeric-missing-summarize.cy.spec.js
+++ b/e2e/test/scenarios/custom-column/reproductions/27745-cc-numeric-missing-summarize.cy.spec.js
@@ -7,6 +7,7 @@ import {
   visualize,
   popover,
   resetTestTable,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 ["postgres", "mysql"].forEach(dialect => {
@@ -39,7 +40,7 @@ import {
 
       visualize();
 
-      cy.findAllByTestId("header-cell").contains("Numeric").click();
+      tableHeaderClick("Numeric");
       popover().findByText(/^Sum$/).click();
 
       cy.wait("@dataset");
diff --git a/e2e/test/scenarios/dashboard-cards/click-behavior.cy.spec.js b/e2e/test/scenarios/dashboard-cards/click-behavior.cy.spec.js
index fd88ff4e7c1..75405a83587 100644
--- a/e2e/test/scenarios/dashboard-cards/click-behavior.cy.spec.js
+++ b/e2e/test/scenarios/dashboard-cards/click-behavior.cy.spec.js
@@ -1406,6 +1406,8 @@ describe("scenarios > dashboard > dashboard cards > click behavior", () => {
           customLinkTextInput.type(`Created at: {{${CREATED_AT_COLUMN_ID}}}`, {
             parseSpecialCharSequences: false,
           });
+          customLinkTextInput.blur();
+
           cy.button("Done").click();
         });
 
diff --git a/e2e/test/scenarios/dashboard-cards/dashboard-card-undo.cy.spec.js b/e2e/test/scenarios/dashboard-cards/dashboard-card-undo.cy.spec.js
index 7f48a02644e..4393d12bc89 100644
--- a/e2e/test/scenarios/dashboard-cards/dashboard-card-undo.cy.spec.js
+++ b/e2e/test/scenarios/dashboard-cards/dashboard-card-undo.cy.spec.js
@@ -78,6 +78,9 @@ describe("scenarios > dashboard cards > undo", () => {
         undo();
         getDashboardCards().should("have.length", cards.length);
         checkOrder();
+        // Seems to be needed to allow the UI to catch up before hovering the next element.
+        // TODO: improve this.
+        cy.wait(200);
       }
 
       createNewTab();
@@ -90,6 +93,7 @@ describe("scenarios > dashboard cards > undo", () => {
         undo();
         getDashboardCards().should("have.length", cards.length);
         checkOrder();
+        cy.wait(200);
       }
     },
   );
diff --git a/e2e/test/scenarios/dashboard-cards/dashboard-drill.cy.spec.js b/e2e/test/scenarios/dashboard-cards/dashboard-drill.cy.spec.js
index 2edbc53209c..bb5b24b4ae1 100644
--- a/e2e/test/scenarios/dashboard-cards/dashboard-drill.cy.spec.js
+++ b/e2e/test/scenarios/dashboard-cards/dashboard-drill.cy.spec.js
@@ -63,9 +63,12 @@ describe("scenarios > dashboard > dashboard drill", () => {
         cy.get("input").first().type("/foo/{{my_number}}/{{my_param}}", {
           parseSpecialCharSequences: false,
         });
-        cy.get("input").last().type("column value: {{my_number}}", {
-          parseSpecialCharSequences: false,
-        });
+        cy.get("input")
+          .last()
+          .type("column value: {{my_number}}", {
+            parseSpecialCharSequences: false,
+          })
+          .blur();
         cy.findByText("Done").click();
       });
 
@@ -299,7 +302,7 @@ describe("scenarios > dashboard > dashboard drill", () => {
         cy.get("input")
           .first()
           .type(`/dashboard/${dashboardId}?my_param=Aaron Hand`);
-        cy.get("input").last().type("Click behavior");
+        cy.get("input").last().type("Click behavior").blur();
         cy.findByText("Done").click();
       });
 
diff --git a/e2e/test/scenarios/dashboard-cards/dashboard-sections.cy.spec.js b/e2e/test/scenarios/dashboard-cards/dashboard-sections.cy.spec.js
index da24849d016..1840e5cd448 100644
--- a/e2e/test/scenarios/dashboard-cards/dashboard-sections.cy.spec.js
+++ b/e2e/test/scenarios/dashboard-cards/dashboard-sections.cy.spec.js
@@ -135,9 +135,11 @@ function selectQuestion(question) {
 }
 
 function overwriteDashCardTitle(dashcardElement, textTitle) {
-  findDashCardAction(dashcardElement, "Show visualization options").click();
+  findDashCardAction(dashcardElement, "Show visualization options").click({
+    force: true,
+  });
   modal().within(() => {
-    cy.findByLabelText("Title").type(`{selectall}{del}${textTitle}`);
+    cy.findByLabelText("Title").type(`{selectall}{del}${textTitle}`).blur();
     cy.button("Done").click();
   });
 }
diff --git a/e2e/test/scenarios/dashboard-cards/dashcard-replace-question.cy.spec.js b/e2e/test/scenarios/dashboard-cards/dashcard-replace-question.cy.spec.js
index 4ad1bd11bf4..6bdfd7579c6 100644
--- a/e2e/test/scenarios/dashboard-cards/dashcard-replace-question.cy.spec.js
+++ b/e2e/test/scenarios/dashboard-cards/dashcard-replace-question.cy.spec.js
@@ -284,7 +284,7 @@ function assertDashCardTitle(title) {
 function overwriteDashCardTitle(dashcardElement, textTitle) {
   findDashCardAction(dashcardElement, "Show visualization options").click();
   modal().within(() => {
-    cy.findByLabelText("Title").type(`{selectall}{del}${textTitle}`);
+    cy.findByLabelText("Title").type(`{selectall}{del}${textTitle}`).blur();
     cy.button("Done").click();
   });
 }
diff --git a/e2e/test/scenarios/dashboard-cards/reproductions/29304-scalar-first-render-size-change.cy.spec.js b/e2e/test/scenarios/dashboard-cards/reproductions/29304-scalar-first-render-size-change.cy.spec.js
index 2f0784839d1..867da244e80 100644
--- a/e2e/test/scenarios/dashboard-cards/reproductions/29304-scalar-first-render-size-change.cy.spec.js
+++ b/e2e/test/scenarios/dashboard-cards/reproductions/29304-scalar-first-render-size-change.cy.spec.js
@@ -67,7 +67,7 @@ describe("issue 29304", () => {
         cy.tick(WAIT_TIME + 1);
 
         const expectedWidth = 130;
-        cy.findByTestId("scalar-value").then(([$scalarValue]) => {
+        cy.findByTestId("scalar-value").should(([$scalarValue]) => {
           expect($scalarValue.offsetWidth).to.be.closeTo(
             expectedWidth,
             expectedWidth * 0.1,
@@ -92,7 +92,7 @@ describe("issue 29304", () => {
         cy.tick(WAIT_TIME + 1);
 
         const expectedWidth = 39;
-        cy.findByTestId("scalar-value").then(([$scalarValue]) => {
+        cy.findByTestId("scalar-value").should(([$scalarValue]) => {
           expect($scalarValue.offsetWidth).to.be.closeTo(
             expectedWidth,
             expectedWidth * 0.1,
diff --git a/e2e/test/scenarios/dashboard-cards/visualization-options.cy.spec.js b/e2e/test/scenarios/dashboard-cards/visualization-options.cy.spec.js
index 4dd2269203f..dfbf1181c52 100644
--- a/e2e/test/scenarios/dashboard-cards/visualization-options.cy.spec.js
+++ b/e2e/test/scenarios/dashboard-cards/visualization-options.cy.spec.js
@@ -32,7 +32,7 @@ describe("scenarios > dashboard cards > visualization options", () => {
     cy.icon("palette").click();
 
     modal().within(() => {
-      cy.findByDisplayValue(originalCardTitle).click().clear();
+      cy.findByDisplayValue(originalCardTitle).click().clear().blur();
       cy.button("Done").click();
     });
 
diff --git a/e2e/test/scenarios/filters/filter-reproductions.cy.spec.js b/e2e/test/scenarios/filters/filter-reproductions.cy.spec.js
index 15a53575afe..cbb25624d7a 100644
--- a/e2e/test/scenarios/filters/filter-reproductions.cy.spec.js
+++ b/e2e/test/scenarios/filters/filter-reproductions.cy.spec.js
@@ -1,6 +1,6 @@
 import { SAMPLE_DB_ID } from "e2e/support/cypress_data";
 import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
-import { restore, popover } from "e2e/support/helpers";
+import { restore, popover, tableHeaderClick } from "e2e/support/helpers";
 
 const { REVIEWS, PEOPLE, PEOPLE_ID } = SAMPLE_DATABASE;
 
@@ -32,7 +32,7 @@ describe("filter bug reproductions", () => {
 
     cy.createQuestion(questionDetails, { visitQuestion: true });
 
-    cy.findByTestId("TableInteractive-root").findByText("Email").click();
+    tableHeaderClick("Email");
 
     popover().within(() => {
       cy.findByText("Filter by this column").click();
diff --git a/e2e/test/scenarios/filters/filter-types.cy.spec.js b/e2e/test/scenarios/filters/filter-types.cy.spec.js
index 808b4c0eeb3..df7a977a5f7 100644
--- a/e2e/test/scenarios/filters/filter-types.cy.spec.js
+++ b/e2e/test/scenarios/filters/filter-types.cy.spec.js
@@ -487,8 +487,8 @@ describe("scenarios > filters > filter types", () => {
             values.forEach(value => {
               cy.findByLabelText("Filter value")
                 .focus()
-                .type(value)
-                .realPress("Tab");
+                .type(`${value},`, { delay: 50 })
+                .blur();
             });
             options.forEach(option => cy.findByText(option).click());
             cy.button("Add filter").click();
@@ -522,7 +522,10 @@ describe("scenarios > filters > filter types", () => {
             .first()
             .within(() => {
               values.forEach(value => {
-                cy.findByLabelText("Filter value").focus().type(value).blur();
+                cy.findByLabelText("Filter value")
+                  .focus()
+                  .type(`${value},`, { delay: 50 })
+                  .blur();
               });
               cy.button("Add filter").click();
             });
diff --git a/e2e/test/scenarios/filters/filter.cy.spec.js b/e2e/test/scenarios/filters/filter.cy.spec.js
index d73c5cfa402..a70c94828ee 100644
--- a/e2e/test/scenarios/filters/filter.cy.spec.js
+++ b/e2e/test/scenarios/filters/filter.cy.spec.js
@@ -23,6 +23,7 @@ import {
   selectFilterOperator,
   expressionEditorWidget,
   cartesianChartCircleWithColors,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 const { ORDERS, ORDERS_ID, PRODUCTS, PRODUCTS_ID, REVIEWS, REVIEWS_ID } =
@@ -298,7 +299,7 @@ describe("scenarios > question > filter", () => {
   it("should offer case expression in the auto-complete suggestions", () => {
     openExpressionEditorFromFreshlyLoadedPage();
 
-    enterCustomColumnDetails({ formula: "c" });
+    enterCustomColumnDetails({ formula: "c", blur: false });
     popover().contains(/case/i);
 
     cy.get("@formula").type("a");
@@ -312,7 +313,7 @@ describe("scenarios > question > filter", () => {
 
     openExpressionEditorFromFreshlyLoadedPage();
 
-    enterCustomColumnDetails({ formula: "c" });
+    enterCustomColumnDetails({ formula: "c", blur: false });
 
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.contains("case")
@@ -338,7 +339,7 @@ describe("scenarios > question > filter", () => {
   it("should highlight the correct matching for suggestions", () => {
     openExpressionEditorFromFreshlyLoadedPage();
 
-    enterCustomColumnDetails({ formula: "[" });
+    enterCustomColumnDetails({ formula: "[", blur: false });
 
     popover().last().findByText("Body");
 
@@ -369,7 +370,7 @@ describe("scenarios > question > filter", () => {
     cy.findByText("Filter").click();
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.findByText("Custom Expression").click();
-    enterCustomColumnDetails({ formula: "su" });
+    enterCustomColumnDetails({ formula: "su", blur: false });
     popover().contains(/Sum of Total/i);
     cy.get("@formula").type("m");
     popover().contains(/Sum of Total/i);
@@ -382,7 +383,6 @@ describe("scenarios > question > filter", () => {
     cy.findByText("Custom Expression").click();
 
     enterCustomColumnDetails({ formula: "NOT IsNull([Rating])" });
-    cy.get("@formula").blur();
 
     cy.button("Done").should("not.be.disabled").click();
 
@@ -391,8 +391,9 @@ describe("scenarios > question > filter", () => {
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.findByText("Custom Expression").click();
 
-    enterCustomColumnDetails({ formula: "NOT IsEmpty([Reviewer])" });
-    cy.get("@formula").blur();
+    enterCustomColumnDetails({
+      formula: "NOT IsEmpty([Reviewer])",
+    });
 
     cy.button("Done").should("not.be.disabled").click();
 
@@ -405,8 +406,7 @@ describe("scenarios > question > filter", () => {
 
   it("should convert 'is empty' on a text column to a custom expression using IsEmpty()", () => {
     openReviewsTable();
-    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.contains("Reviewer").click();
+    tableHeaderClick("Reviewer");
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.findByText("Filter by this column").click();
     selectFilterOperator("Is empty");
@@ -425,7 +425,7 @@ describe("scenarios > question > filter", () => {
     cy.findByText("Custom Expression").click();
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.contains("isempty([Reviewer])");
-    cy.get(".ace_text-input").clear().type("NOT IsEmpty([Reviewer])");
+    cy.get(".ace_text-input").clear().type("NOT IsEmpty([Reviewer])").blur();
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.findByText("Done").click();
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
@@ -434,8 +434,7 @@ describe("scenarios > question > filter", () => {
 
   it("should convert 'is empty' on a numeric column to a custom expression using IsNull()", () => {
     openReviewsTable();
-    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.contains("Rating").click();
+    tableHeaderClick("Rating");
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.findByText("Filter by this column").click();
     selectFilterOperator("Is empty");
@@ -456,7 +455,8 @@ describe("scenarios > question > filter", () => {
     cy.contains("isnull([Rating])");
     cy.get(".ace_text-input")
       .clear()
-      .type("NOT IsNull([Rating])", { delay: 50 });
+      .type("NOT IsNull([Rating])", { delay: 50 })
+      .blur();
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.findByText("Done").click();
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
@@ -597,7 +597,6 @@ describe("scenarios > question > filter", () => {
     cy.findByText("Custom Expression").click();
 
     enterCustomColumnDetails({ formula: "3.14159" });
-    cy.get("@formula").blur();
 
     cy.button("Done").should("be.disabled");
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
@@ -611,7 +610,6 @@ describe("scenarios > question > filter", () => {
     cy.findByText("Custom Expression").click();
 
     enterCustomColumnDetails({ formula: '"TheAnswer"' });
-    cy.get("@formula").blur();
 
     cy.button("Done").should("be.disabled");
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
@@ -650,7 +648,6 @@ describe("scenarios > question > filter", () => {
     popover().within(() => {
       cy.findByText("Custom Expression").click();
       enterCustomColumnDetails({ formula: "[Total] < [Subtotal]" });
-      cy.get("@formula").blur();
       cy.button("Done").click();
     });
 
@@ -933,10 +930,7 @@ describe("scenarios > question > filter", () => {
       beforeEach(setupBooleanQuery);
 
       it("from the column popover (metabase#16386-1)", () => {
-        cy.findAllByTestId("header-cell")
-          .contains("boolean")
-          .should("be.visible")
-          .click();
+        tableHeaderClick("boolean");
 
         popover().findByText("Filter by this column").click();
 
@@ -977,7 +971,6 @@ describe("scenarios > question > filter", () => {
         popover().contains("Custom Expression").click();
         expressionEditorWidget().within(() => {
           enterCustomColumnDetails({ formula: `boolean = ${condition}` });
-          cy.get("@formula").blur();
 
           cy.button("Done").click();
         });
@@ -1026,7 +1019,6 @@ describe("scenarios > question > filter", () => {
       popover().contains("Custom Expression").click();
       expressionEditorWidget().within(() => {
         enterCustomColumnDetails({ formula: "boolean = true" });
-        cy.get("@formula").blur();
 
         cy.button("Done").click();
       });
diff --git a/e2e/test/scenarios/filters/relative-datetime.cy.spec.js b/e2e/test/scenarios/filters/relative-datetime.cy.spec.js
index ec77b99d537..b262012536f 100644
--- a/e2e/test/scenarios/filters/relative-datetime.cy.spec.js
+++ b/e2e/test/scenarios/filters/relative-datetime.cy.spec.js
@@ -5,6 +5,7 @@ import {
   popover,
   openOrdersTable,
   queryBuilderMain,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 const STARTING_FROM_UNITS = [
@@ -62,7 +63,7 @@ describe("scenarios > question > relative-datetime", () => {
     it("should not clobber filter when value is set to 1", () => {
       openOrdersTable();
 
-      queryBuilderMain().findByText("Created At").click();
+      tableHeaderClick("Created At");
 
       popover().within(() => {
         cy.findByText("Filter by this column").click();
@@ -95,7 +96,7 @@ describe("scenarios > question > relative-datetime", () => {
     it("starting from should contain units only equal or greater than the filter unit", () => {
       openOrdersTable();
 
-      cy.findByTextEnsureVisible("Created At").click();
+      tableHeaderClick("Created At");
       popover().within(() => {
         cy.findByText("Filter by this column").click();
         cy.findByText("Relative dates…").click();
@@ -122,7 +123,7 @@ describe("scenarios > question > relative-datetime", () => {
     it("should go back to shortcuts view", () => {
       openOrdersTable();
 
-      cy.findByTextEnsureVisible("Created At").click();
+      tableHeaderClick("Created At");
       popover().within(() => {
         cy.findByText("Filter by this column").click();
         cy.findByText("Specific dates…").click();
@@ -135,7 +136,7 @@ describe("scenarios > question > relative-datetime", () => {
     it("current filters should work (metabase#21977)", () => {
       openOrdersTable();
 
-      queryBuilderMain().findByText("Created At").click();
+      tableHeaderClick("Created At");
       popover().within(() => {
         cy.findByText("Filter by this column").click();
         cy.findByText("Relative dates…").click();
@@ -156,7 +157,7 @@ describe("scenarios > question > relative-datetime", () => {
     it("Relative dates should default to past filter (metabase#22027)", () => {
       openOrdersTable();
 
-      cy.findByTextEnsureVisible("Created At").click();
+      tableHeaderClick("Created At");
       popover().within(() => {
         cy.findByText("Filter by this column").click();
         cy.findByText("Relative dates…").click();
@@ -257,7 +258,7 @@ const nativeSQL = values => {
 };
 
 const openCreatedAt = tab => {
-  cy.findByTextEnsureVisible("Created At").click();
+  tableHeaderClick("Created At");
   popover().within(() => {
     cy.findByText("Filter by this column").click();
     cy.findByText("Relative dates…").click();
@@ -296,7 +297,7 @@ const setStartingFromValue = value => {
 };
 
 const withStartingFrom = (dir, [num, unit], [startNum, startUnit]) => {
-  cy.findByTextEnsureVisible("testcol").click();
+  tableHeaderClick("testcol");
   cy.findByTextEnsureVisible("Filter by this column").click();
   cy.findByTextEnsureVisible("Relative dates…").click();
   popover().within(() => {
diff --git a/e2e/test/scenarios/filters/reproductions/16621-create-multiple-filters-with-same-value.cy.spec.js b/e2e/test/scenarios/filters/reproductions/16621-create-multiple-filters-with-same-value.cy.spec.js
index 7d6268750a2..17db4312c88 100644
--- a/e2e/test/scenarios/filters/reproductions/16621-create-multiple-filters-with-same-value.cy.spec.js
+++ b/e2e/test/scenarios/filters/reproductions/16621-create-multiple-filters-with-same-value.cy.spec.js
@@ -1,4 +1,9 @@
-import { restore, openProductsTable, popover } from "e2e/support/helpers";
+import {
+  restore,
+  openProductsTable,
+  popover,
+  tableHeaderClick,
+} from "e2e/support/helpers";
 
 describe("issue 16621", () => {
   beforeEach(() => {
@@ -8,8 +13,7 @@ describe("issue 16621", () => {
   });
 
   it("should be possible to create multiple filter that start with the same value (metabase#16621)", () => {
-    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.findByText("Category").click();
+    tableHeaderClick("Category");
     popover().within(() => {
       cy.findByText("Filter by this column").click();
       cy.findByPlaceholderText("Search the list").type("Gadget");
diff --git a/e2e/test/scenarios/filters/reproductions/22730-table-column-time-filter.cy.spec.js b/e2e/test/scenarios/filters/reproductions/22730-table-column-time-filter.cy.spec.js
index dd760204534..8511ee9682f 100644
--- a/e2e/test/scenarios/filters/reproductions/22730-table-column-time-filter.cy.spec.js
+++ b/e2e/test/scenarios/filters/reproductions/22730-table-column-time-filter.cy.spec.js
@@ -1,4 +1,4 @@
-import { restore, popover } from "e2e/support/helpers";
+import { restore, popover, tableHeaderClick } from "e2e/support/helpers";
 
 describe("issue 22730", () => {
   beforeEach(() => {
@@ -24,10 +24,7 @@ describe("issue 22730", () => {
     cy.findByText("Explore results").click();
     cy.wait("@dataset");
 
-    cy.findAllByTestId("header-cell")
-      .contains("time")
-      .should("be.visible")
-      .click();
+    tableHeaderClick("time");
 
     popover().within(() => {
       cy.findByText("Filter by this column").click();
diff --git a/e2e/test/scenarios/filters/reproductions/24664-multiple-filters-editing.cy.spec.js b/e2e/test/scenarios/filters/reproductions/24664-multiple-filters-editing.cy.spec.js
index a1b6fca948f..864bc4df5bc 100644
--- a/e2e/test/scenarios/filters/reproductions/24664-multiple-filters-editing.cy.spec.js
+++ b/e2e/test/scenarios/filters/reproductions/24664-multiple-filters-editing.cy.spec.js
@@ -1,4 +1,9 @@
-import { restore, openProductsTable, popover } from "e2e/support/helpers";
+import {
+  restore,
+  openProductsTable,
+  popover,
+  tableHeaderClick,
+} from "e2e/support/helpers";
 
 describe("issue 24664", () => {
   beforeEach(() => {
@@ -8,16 +13,14 @@ describe("issue 24664", () => {
   });
 
   it("should be possible to create multiple filter that start with the same value (metabase#24664)", () => {
-    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.findByText("Category").click();
+    tableHeaderClick("Category");
     popover().within(() => {
       cy.findByText("Filter by this column").click();
       cy.findByText("Doohickey").click();
       cy.button("Add filter").click();
     });
 
-    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.findByText("Category").click();
+    tableHeaderClick("Category");
     popover().within(() => {
       cy.findByText("Filter by this column").click();
       cy.findByText("Gizmo").click();
diff --git a/e2e/test/scenarios/filters/reproductions/25927-column-filters-not-working-after-cc.cy.spec.js b/e2e/test/scenarios/filters/reproductions/25927-column-filters-not-working-after-cc.cy.spec.js
index 765cf23381b..6eb7d131528 100644
--- a/e2e/test/scenarios/filters/reproductions/25927-column-filters-not-working-after-cc.cy.spec.js
+++ b/e2e/test/scenarios/filters/reproductions/25927-column-filters-not-working-after-cc.cy.spec.js
@@ -1,6 +1,11 @@
 import { SAMPLE_DB_ID } from "e2e/support/cypress_data";
 import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
-import { popover, restore, visitQuestionAdhoc } from "e2e/support/helpers";
+import {
+  popover,
+  restore,
+  tableHeaderClick,
+  visitQuestionAdhoc,
+} from "e2e/support/helpers";
 
 const { ORDERS, ORDERS_ID } = SAMPLE_DATABASE;
 
@@ -30,7 +35,7 @@ describe("issue 25927", () => {
   });
 
   it("column filter should work for questions with custom column (metabase#25927)", () => {
-    cy.findAllByTestId("header-cell").contains("Created At: Month").click();
+    tableHeaderClick("Created At: Month");
     popover().within(() => {
       cy.findByText("Filter by this column").click();
       cy.findByText("Last 30 days").click();
diff --git a/e2e/test/scenarios/filters/reproductions/25990-filter-nested-join.cy.spec.js b/e2e/test/scenarios/filters/reproductions/25990-filter-nested-join.cy.spec.js
index 198008dd461..8fffb16662a 100644
--- a/e2e/test/scenarios/filters/reproductions/25990-filter-nested-join.cy.spec.js
+++ b/e2e/test/scenarios/filters/reproductions/25990-filter-nested-join.cy.spec.js
@@ -50,7 +50,7 @@ describe("issue 25990", () => {
 
     modal().within(() => {
       cy.findByText("Person").click();
-      cy.findByPlaceholderText("Enter an ID").type("10");
+      cy.findByPlaceholderText("Enter an ID").type("10").blur();
       cy.button("Apply filters").click();
     });
 
diff --git a/e2e/test/scenarios/filters/reproductions/27123-exclude-always-shows-days-of-week.cy.spec.js b/e2e/test/scenarios/filters/reproductions/27123-exclude-always-shows-days-of-week.cy.spec.js
index 857b3195328..3c43384c021 100644
--- a/e2e/test/scenarios/filters/reproductions/27123-exclude-always-shows-days-of-week.cy.spec.js
+++ b/e2e/test/scenarios/filters/reproductions/27123-exclude-always-shows-days-of-week.cy.spec.js
@@ -1,5 +1,5 @@
 import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
-import { restore, popover } from "e2e/support/helpers";
+import { restore, popover, tableHeaderClick } from "e2e/support/helpers";
 
 const { ORDERS_ID } = SAMPLE_DATABASE;
 
@@ -19,7 +19,7 @@ describe("issue 27123", () => {
   });
 
   it("exclude filter should not resolve to 'Days of the week' regardless of the chosen granularity  (metabase#27123)", () => {
-    cy.findAllByTestId("header-cell").contains("Created At").click();
+    tableHeaderClick("Created At");
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.findByText("Filter by this column").click();
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
diff --git a/e2e/test/scenarios/filters/reproductions/30312-drill-filter-on-aggregated-column.cy.spec.js b/e2e/test/scenarios/filters/reproductions/30312-drill-filter-on-aggregated-column.cy.spec.js
index 6a408c9002e..1292acc9a96 100644
--- a/e2e/test/scenarios/filters/reproductions/30312-drill-filter-on-aggregated-column.cy.spec.js
+++ b/e2e/test/scenarios/filters/reproductions/30312-drill-filter-on-aggregated-column.cy.spec.js
@@ -4,6 +4,7 @@ import {
   queryBuilderMain,
   restore,
   selectFilterOperator,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 const { ORDERS, ORDERS_ID } = SAMPLE_DATABASE;
@@ -37,10 +38,9 @@ describe("issue 30312", () => {
       { visitQuestion: true },
     );
 
-    cy.findAllByTestId("header-cell")
-      .eq(1)
-      .should("have.text", "Count")
-      .click();
+    cy.findAllByTestId("header-cell").eq(1).should("have.text", "Count");
+
+    tableHeaderClick("Count");
 
     popover().findByText("Filter by this column").click();
     selectFilterOperator("Equal to");
diff --git a/e2e/test/scenarios/filters/reproductions/31340-long-column-name-search-results.cy.spec.js b/e2e/test/scenarios/filters/reproductions/31340-long-column-name-search-results.cy.spec.js
index 8d0f87672c4..7235ede263c 100644
--- a/e2e/test/scenarios/filters/reproductions/31340-long-column-name-search-results.cy.spec.js
+++ b/e2e/test/scenarios/filters/reproductions/31340-long-column-name-search-results.cy.spec.js
@@ -1,6 +1,11 @@
 import { SAMPLE_DB_ID, SAMPLE_DB_SCHEMA_ID } from "e2e/support/cypress_data";
 import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
-import { popover, restore, selectFilterOperator } from "e2e/support/helpers";
+import {
+  popover,
+  restore,
+  selectFilterOperator,
+  tableHeaderClick,
+} from "e2e/support/helpers";
 
 const { PEOPLE_ID } = SAMPLE_DATABASE;
 
@@ -38,7 +43,7 @@ describe("issue 31340", () => {
   });
 
   it("should properly display long column names in filter options search results (metabase#31340)", () => {
-    cy.findAllByTestId("header-cell").contains(LONG_COLUMN_NAME).click();
+    tableHeaderClick(LONG_COLUMN_NAME);
 
     popover().findByText("Filter by this column").click();
     selectFilterOperator("Is");
diff --git a/e2e/test/scenarios/filters/reproductions/9339-clipboard-numeric-filter.cy.spec.js b/e2e/test/scenarios/filters/reproductions/9339-clipboard-numeric-filter.cy.spec.js
index 8aaccfcdca5..2f591dfb481 100644
--- a/e2e/test/scenarios/filters/reproductions/9339-clipboard-numeric-filter.cy.spec.js
+++ b/e2e/test/scenarios/filters/reproductions/9339-clipboard-numeric-filter.cy.spec.js
@@ -2,6 +2,7 @@ import {
   openOrdersTable,
   restore,
   selectFilterOperator,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 describe("issue 9339", () => {
@@ -13,8 +14,7 @@ describe("issue 9339", () => {
   it("should not paste non-numeric values into single-value numeric filters (metabase#9339)", () => {
     openOrdersTable();
 
-    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.findByText("Total").click();
+    tableHeaderClick("Total");
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.findByText("Filter by this column").click();
     selectFilterOperator("Greater than");
diff --git a/e2e/test/scenarios/models/models.cy.spec.js b/e2e/test/scenarios/models/models.cy.spec.js
index 5931d92e728..9a3441f1c7e 100644
--- a/e2e/test/scenarios/models/models.cy.spec.js
+++ b/e2e/test/scenarios/models/models.cy.spec.js
@@ -33,6 +33,7 @@ import {
   entityPickerModal,
   questionInfoButton,
   entityPickerModalTab,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 import {
@@ -391,8 +392,7 @@ describe("scenarios > models", () => {
       cy.visit(`/model/${ORDERS_QUESTION_ID}`);
       cy.wait("@dataset");
 
-      // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-      cy.findByText("Subtotal").click();
+      tableHeaderClick("Subtotal");
       selectFromDropdown("Sum over time");
 
       assertQuestionIsBasedOnModel({
diff --git a/e2e/test/scenarios/models/reproductions.cy.spec.js b/e2e/test/scenarios/models/reproductions.cy.spec.js
index a000279c8e9..9495803b484 100644
--- a/e2e/test/scenarios/models/reproductions.cy.spec.js
+++ b/e2e/test/scenarios/models/reproductions.cy.spec.js
@@ -50,6 +50,7 @@ import {
   openNotebook,
   visualize,
   focusNativeEditor,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 import {
   createMockActionParameter,
@@ -607,8 +608,7 @@ describe("filtering based on the remapped column name should result in a correct
   });
 
   it("when done through the column header action (metabase#22715-1)", () => {
-    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.findByText("Created At").click();
+    tableHeaderClick("Created At");
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.findByText("Filter by this column").click();
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
diff --git a/e2e/test/scenarios/models/reproductions.cy.spec.ts b/e2e/test/scenarios/models/reproductions.cy.spec.ts
index aef7cbcc3bf..969c791b28d 100644
--- a/e2e/test/scenarios/models/reproductions.cy.spec.ts
+++ b/e2e/test/scenarios/models/reproductions.cy.spec.ts
@@ -6,6 +6,7 @@ import {
   restore,
   hovercard,
   createNativeQuestion,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 import type { FieldReference } from "metabase-types/api";
 
@@ -85,13 +86,16 @@ describe("issue 29943", () => {
 
     assertColumnSelected(0, "ID");
 
-    getHeaderCell(1, "Custom").click();
+    getHeaderCell(1, "Custom");
+    tableHeaderClick("Custom");
     assertColumnSelected(1, "Custom");
 
-    getHeaderCell(2, "Total").click();
+    getHeaderCell(2, "Total");
+    tableHeaderClick("Total");
     assertColumnSelected(2, "Total");
 
-    getHeaderCell(0, "ID").click();
+    getHeaderCell(0, "ID");
+    tableHeaderClick("ID");
     assertColumnSelected(0, "ID");
   });
 });
@@ -240,10 +244,10 @@ describe("issues 25884 and 34349", () => {
 
     cy.findByLabelText("Description").should("have.text", ID_DESCRIPTION);
 
-    cy.findAllByTestId("header-cell").contains("Country").click();
+    tableHeaderClick("Country");
     cy.findByLabelText("Description").should("have.text", "");
 
-    cy.findAllByTestId("header-cell").contains("ID").click();
+    tableHeaderClick("ID");
     cy.findByLabelText("Description").should("have.text", ID_DESCRIPTION);
   });
 });
diff --git a/e2e/test/scenarios/native-filters/helpers/e2e-field-filter-helpers.js b/e2e/test/scenarios/native-filters/helpers/e2e-field-filter-helpers.js
index 3732557aedb..f31f4aad924 100644
--- a/e2e/test/scenarios/native-filters/helpers/e2e-field-filter-helpers.js
+++ b/e2e/test/scenarios/native-filters/helpers/e2e-field-filter-helpers.js
@@ -109,9 +109,12 @@ export function addWidgetNumberFilter(
  * @return {function}
  */
 export function addDefaultNumberFilter(value) {
-  return isBetweenFilter(value)
-    ? addBetweenFilter(value)
-    : enterDefaultValue(value);
+  if (isBetweenFilter(value)) {
+    cy.findByText("Enter a default value…").click();
+    addBetweenFilter(value);
+  } else {
+    enterDefaultValue(value);
+  }
 }
 
 // UI PATTERNS
@@ -180,7 +183,9 @@ function addSimpleNumberFilter(value, buttonLabel = "Add filter") {
  */
 function enterDefaultValue(value, buttonLabel = "Add filter") {
   cy.findByText("Enter a default value…").click();
-  cy.findByPlaceholderText("Enter a default value…").type(`${value}{enter}`);
+  cy.findByPlaceholderText("Enter a default value…")
+    .type(`${value}{enter}`)
+    .blur();
   cy.button(buttonLabel).click();
 }
 
diff --git a/e2e/test/scenarios/native-filters/reproductions/31606.cy.spec.js b/e2e/test/scenarios/native-filters/reproductions/31606.cy.spec.js
index 35cce6acc1a..bc2e15761a4 100644
--- a/e2e/test/scenarios/native-filters/reproductions/31606.cy.spec.js
+++ b/e2e/test/scenarios/native-filters/reproductions/31606.cy.spec.js
@@ -76,7 +76,6 @@ describe("issue 31606", { tags: "@external" }, () => {
       .should("have.value", "ID")
       .should("be.disabled");
 
-    FieldFilter.openEntryForm({ isFilterRequired: true });
     FieldFilter.addDefaultStringFilter("2");
 
     cy.findByTestId("sidebar-content").within(() => {
@@ -97,7 +96,8 @@ describe("issue 31606", { tags: "@external" }, () => {
     });
 
     // Field Filter
-    SQLFilter.setWidgetValue("23");
+    filterWidget().click();
+    popover().findByPlaceholderText("Enter an ID").type("23");
     popover().findByText("Add filter").click();
 
     filterWidget().within(() => {
diff --git a/e2e/test/scenarios/native-filters/sql-field-filter-number.cy.spec.js b/e2e/test/scenarios/native-filters/sql-field-filter-number.cy.spec.js
index 33dc8f15303..13f6198541f 100644
--- a/e2e/test/scenarios/native-filters/sql-field-filter-number.cy.spec.js
+++ b/e2e/test/scenarios/native-filters/sql-field-filter-number.cy.spec.js
@@ -51,7 +51,6 @@ describe("scenarios > filters > sql filters > field filter > Number", () => {
 
         FieldFilter.setWidgetType(subType);
 
-        FieldFilter.openEntryForm({ isFilterRequired: true });
         FieldFilter.addDefaultNumberFilter(value);
 
         SQLFilter.runQuery();
diff --git a/e2e/test/scenarios/native-filters/sql-field-filter-string.cy.spec.js b/e2e/test/scenarios/native-filters/sql-field-filter-string.cy.spec.js
index 64bfa154e7b..534baffa6cf 100644
--- a/e2e/test/scenarios/native-filters/sql-field-filter-string.cy.spec.js
+++ b/e2e/test/scenarios/native-filters/sql-field-filter-string.cy.spec.js
@@ -58,8 +58,6 @@ describe("scenarios > filters > sql filters > field filter > String", () => {
       ([subType, { searchTerm, value, representativeResult }], index) => {
         FieldFilter.setWidgetType(subType);
 
-        FieldFilter.openEntryForm({ isFilterRequired: true });
-
         searchTerm
           ? FieldFilter.pickDefaultValue(searchTerm, value)
           : FieldFilter.addDefaultStringFilter(value);
diff --git a/e2e/test/scenarios/native/reproductions/34330-autocompletion-triggers-too-eagerly-on-initial-input.cy.spec.js b/e2e/test/scenarios/native/reproductions/34330-autocompletion-triggers-too-eagerly-on-initial-input.cy.spec.js
index 9f88bef3b83..df0d8290fc4 100644
--- a/e2e/test/scenarios/native/reproductions/34330-autocompletion-triggers-too-eagerly-on-initial-input.cy.spec.js
+++ b/e2e/test/scenarios/native/reproductions/34330-autocompletion-triggers-too-eagerly-on-initial-input.cy.spec.js
@@ -13,7 +13,8 @@ describe("issue 34330", () => {
     const editor = openNativeEditor();
 
     // can't use cy.type because it does not simulate the bug
-    editor.type("USER_");
+    // Delay needed for React 18. TODO: fix shame
+    editor.type("USER").type("_", { delay: 1000 });
 
     cy.wait("@autocomplete").then(({ request }) => {
       const url = new URL(request.url);
diff --git a/e2e/test/scenarios/onboarding/command-palette.cy.spec.js b/e2e/test/scenarios/onboarding/command-palette.cy.spec.js
index 3fc49b13603..070f6a33eaa 100644
--- a/e2e/test/scenarios/onboarding/command-palette.cy.spec.js
+++ b/e2e/test/scenarios/onboarding/command-palette.cy.spec.js
@@ -26,7 +26,7 @@ describe("command palette", () => {
   });
 
   it("should render a searchable command palette", () => {
-    //Add a description for a check
+    // //Add a description for a check
     cy.request("PUT", `/api/card/${ORDERS_COUNT_QUESTION_ID}`, {
       description: "The best question",
     });
@@ -200,7 +200,7 @@ describe("command palette", () => {
     cy.get("@database").should("be.null");
     cy.get("@search").should("be.null");
 
-    cy.findByLabelText("Email address").type(admin.email);
+    cy.findByLabelText(/Email address/).type(admin.email);
     cy.findByLabelText("Password").type(admin.password);
     cy.button("Sign in").click();
     cy.findByTestId("greeting-message");
diff --git a/e2e/test/scenarios/onboarding/reference/databases.cy.spec.js b/e2e/test/scenarios/onboarding/reference/databases.cy.spec.js
index fe47713963a..1d4e4862fe1 100644
--- a/e2e/test/scenarios/onboarding/reference/databases.cy.spec.js
+++ b/e2e/test/scenarios/onboarding/reference/databases.cy.spec.js
@@ -26,15 +26,12 @@ describe("scenarios > reference > databases", () => {
 
   it("should let an admin edit details about the database", () => {
     cy.visit("/reference/databases/1");
-    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.contains("Edit").click();
+
+    // For some unknown reason, calling .click() causes the form to immediately reset, putting us
+    // in a state like we never clicked the edit button TODO: Fix
+    cy.button(/Edit/).trigger("click");
     // Q - is there any cleaner way to get a nearby element without having to know the DOM?
-    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.contains("Description")
-      .parent()
-      .parent()
-      .find("textarea")
-      .type("A pretty ok store");
+    cy.findByPlaceholderText("No description yet").type("A pretty ok store");
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.contains("Save").click();
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
@@ -43,15 +40,13 @@ describe("scenarios > reference > databases", () => {
 
   it("should let an admin start to edit and cancel without saving", () => {
     cy.visit("/reference/databases/1");
-    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.contains("Edit").click();
+    // For some unknown reason, calling .click() causes the form to immediately reset, putting us
+    // in a state like we never clicked the edit button TODO: Fix
+    cy.button(/Edit/).trigger("click");
     // Q - is there any cleaner way to get a nearby element without having to know the DOM?
-    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.contains("Why this")
-      .parent()
-      .parent()
-      .find("textarea")
-      .type("Turns out it's not");
+    cy.findByPlaceholderText("Nothing interesting yet").type(
+      "Turns out it's not",
+    );
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.contains("Cancel").click();
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
@@ -60,8 +55,10 @@ describe("scenarios > reference > databases", () => {
 
   it("should let an admin edit the database name", () => {
     cy.visit("/reference/databases/1");
-    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.contains("Edit").click();
+    // For some unknown reason, calling .click() causes the form to immediately reset, putting us
+    // in a state like we never clicked the edit button TODO: Fix
+    cy.button(/Edit/).trigger("click");
+
     cy.findByPlaceholderText("Sample Database")
       .clear()
       .type("My definitely profitable business");
diff --git a/e2e/test/scenarios/onboarding/reference/reproductions/5276-remove-field-type.cy.spec.js b/e2e/test/scenarios/onboarding/reference/reproductions/5276-remove-field-type.cy.spec.js
index 090e969169e..57df2fc141e 100644
--- a/e2e/test/scenarios/onboarding/reference/reproductions/5276-remove-field-type.cy.spec.js
+++ b/e2e/test/scenarios/onboarding/reference/reproductions/5276-remove-field-type.cy.spec.js
@@ -18,8 +18,10 @@ describe("issue 5276", () => {
     cy.findByText("Products").click();
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.findByText("Fields in this table").click();
-    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.findByText("Edit").click();
+
+    // Calling .click on this element goes into edit more and immediately calls resetForm to pull us back out
+    // no idea why. TODO: Fix
+    cy.button(/Edit/).trigger("click");
 
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.findByText("Score").click();
diff --git a/e2e/test/scenarios/question-reproductions/reproductions-1.cy.spec.js b/e2e/test/scenarios/question-reproductions/reproductions-1.cy.spec.js
index 5d94986e2b2..32deb153830 100644
--- a/e2e/test/scenarios/question-reproductions/reproductions-1.cy.spec.js
+++ b/e2e/test/scenarios/question-reproductions/reproductions-1.cy.spec.js
@@ -24,7 +24,6 @@ import {
   saveDashboard,
   editDashboard,
   visitDashboard,
-  openColumnOptions,
   questionInfoButton,
   rightSidebar,
   getNotebookStep,
@@ -35,6 +34,7 @@ import {
   openProductsTable,
   mockSessionProperty,
   visitQuestionAdhoc,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 import { setAdHocFilter } from "../native-filters/helpers/e2e-date-filter-helpers";
@@ -639,7 +639,7 @@ describe("issue 17514", () => {
       });
 
       // Cypress cannot click elements that are blocked by an overlay so this will immediately fail if the issue is not fixed
-      openColumnOptions("Subtotal");
+      tableHeaderClick("Subtotal");
       // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
       cy.findByText("Filter by this column");
     });
diff --git a/e2e/test/scenarios/question-reproductions/reproductions-2.cy.spec.js b/e2e/test/scenarios/question-reproductions/reproductions-2.cy.spec.js
index 535bd19b04b..6f40bf8a78a 100644
--- a/e2e/test/scenarios/question-reproductions/reproductions-2.cy.spec.js
+++ b/e2e/test/scenarios/question-reproductions/reproductions-2.cy.spec.js
@@ -23,6 +23,7 @@ import {
   queryBuilderHeader,
   describeOSS,
   saveQuestion,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 const { ORDERS, ORDERS_ID, PRODUCTS, PRODUCTS_ID, PEOPLE } = SAMPLE_DATABASE;
@@ -214,7 +215,7 @@ describe("issue 24839: should be able to summarize a nested question based on th
   });
 
   it("from a table header cell (metabase#24839-2)", () => {
-    cy.findAllByTestId("header-cell").contains("Average of Total").click();
+    tableHeaderClick("Average of Total");
 
     popover().contains("Distinct values").click();
 
@@ -258,8 +259,7 @@ describe("issue 25016", () => {
 
   it("should be possible to filter by a column in a multi-stage query (metabase#25016)", () => {
     visitQuestionAdhoc(questionDetails);
-    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.findByText("Category").click();
+    tableHeaderClick("Category");
 
     popover().within(() => {
       cy.findByText("Filter by this column").click();
@@ -738,7 +738,7 @@ describe("Custom columns visualization settings", () => {
   });
 
   it("should not show 'Save' after saving viz settings from the custom column dropdown", () => {
-    cy.findAllByTestId("header-cell").contains(EXPRESSION_NAME).click();
+    tableHeaderClick(EXPRESSION_NAME);
     popover().within(() => {
       cy.findByRole("button", { name: /gear icon/i }).click();
     });
diff --git a/e2e/test/scenarios/question/nested.cy.spec.js b/e2e/test/scenarios/question/nested.cy.spec.js
index 31ee44c8c2e..f2b87de03b1 100644
--- a/e2e/test/scenarios/question/nested.cy.spec.js
+++ b/e2e/test/scenarios/question/nested.cy.spec.js
@@ -15,6 +15,7 @@ import {
   assertQueryBuilderRowCount,
   entityPickerModal,
   entityPickerModalTab,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 const { ORDERS, ORDERS_ID, PRODUCTS, PRODUCTS_ID, PEOPLE } = SAMPLE_DATABASE;
@@ -63,7 +64,7 @@ describe("scenarios > question > nested", () => {
       { loadBaseQuestionMetadata: true },
     );
 
-    openHeaderCellContextMenu("Count");
+    tableHeaderClick("Count");
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.contains("Distribution").click();
     cy.wait("@dataset");
@@ -75,7 +76,7 @@ describe("scenarios > question > nested", () => {
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.findByText("Nested GUI").click();
 
-    openHeaderCellContextMenu("Count");
+    tableHeaderClick("Count");
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.contains("Sum over time").click();
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
@@ -101,7 +102,7 @@ describe("scenarios > question > nested", () => {
       { loadBaseQuestionMetadata: true },
     );
 
-    openHeaderCellContextMenu("COUNT");
+    tableHeaderClick("COUNT");
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.contains("Distribution").click();
     cy.wait("@dataset");
@@ -112,7 +113,7 @@ describe("scenarios > question > nested", () => {
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.findByText("Nested SQL").click();
 
-    openHeaderCellContextMenu("COUNT");
+    tableHeaderClick("COUNT");
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.contains("Sum over time").click();
     cy.wait("@dataset");
@@ -260,8 +261,7 @@ describe("scenarios > question > nested", () => {
     createNestedQuestion({ baseQuestionDetails });
 
     // The column title
-    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.findByText("Products → Category").click();
+    tableHeaderClick("Products → Category");
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.findByText("Distribution").click();
     cy.wait("@dataset");
@@ -625,12 +625,3 @@ function visitNestedQueryAdHoc(id) {
     },
   });
 }
-
-function openHeaderCellContextMenu(cell) {
-  cy.findByTestId("TableInteractive-root").within(() => {
-    cy.findAllByTestId("header-cell")
-      .should("be.visible")
-      .contains(cell)
-      .click();
-  });
-}
diff --git a/e2e/test/scenarios/question/new.cy.spec.js b/e2e/test/scenarios/question/new.cy.spec.js
index f6b8c12e12b..8537ad36ccf 100644
--- a/e2e/test/scenarios/question/new.cy.spec.js
+++ b/e2e/test/scenarios/question/new.cy.spec.js
@@ -26,6 +26,7 @@ import {
   modal,
   pickEntity,
   visitQuestion,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 const { ORDERS, ORDERS_ID } = SAMPLE_DATABASE;
@@ -215,8 +216,10 @@ describe("scenarios > question > new", () => {
 
     cy.get(".test-TableInteractive-cellWrapper--lastColumn") // Quantity (last in the default order for Sample Database)
       .eq(1) // first table body cell
-      .should("contain", "2") // quantity for order ID#1
-      .click();
+      .should("contain", "2"); // quantity for order ID#1
+
+    // Test was flaky due to long chain.
+    cy.get(".test-TableInteractive-cellWrapper--lastColumn").eq(1).click();
     cy.wait("@dataset");
 
     cy.get(
@@ -346,7 +349,7 @@ describe("scenarios > question > new", () => {
     cy.findByDisplayValue(originalQuestionName).should("exist");
 
     cy.log("Change anything about this question to make it dirty");
-    cy.findByTestId("header-cell").should("have.text", "Count").click();
+    tableHeaderClick("Count");
     popover().icon("arrow_down").click();
 
     cy.findByTestId("qb-header-action-panel").button("Save").click();
diff --git a/e2e/test/scenarios/question/notebook.cy.spec.js b/e2e/test/scenarios/question/notebook.cy.spec.js
index 00f7aa16ddb..40bad618a91 100644
--- a/e2e/test/scenarios/question/notebook.cy.spec.js
+++ b/e2e/test/scenarios/question/notebook.cy.spec.js
@@ -176,7 +176,6 @@ describe("scenarios > question > notebook", { tags: "@slow" }, () => {
     cy.findByText("Custom Expression").click();
 
     enterCustomColumnDetails({ formula: "[Price] > 1" });
-    cy.get("@formula").blur();
 
     cy.button("Done").click();
 
@@ -361,7 +360,6 @@ describe("scenarios > question > notebook", { tags: "@slow" }, () => {
       cy.findByText("Custom Expression").click();
 
       enterCustomColumnDetails({ formula: "[Subtotal] - Tax > 140" });
-      cy.get("@formula").blur();
 
       // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
       cy.contains(/^redundant input/i).should("not.exist");
@@ -933,7 +931,7 @@ function assertTableRowCount(expectedCount) {
 }
 
 function addSimpleCustomColumn(name) {
-  enterCustomColumnDetails({ formula: "C" });
+  enterCustomColumnDetails({ formula: "C", blur: false });
   cy.findByText("ategory").click();
   cy.findByPlaceholderText("Something nice and descriptive").click().type(name);
   cy.button("Done").click();
diff --git a/e2e/test/scenarios/question/saved.cy.spec.js b/e2e/test/scenarios/question/saved.cy.spec.js
index 40b90d911a8..5d97b555dc7 100644
--- a/e2e/test/scenarios/question/saved.cy.spec.js
+++ b/e2e/test/scenarios/question/saved.cy.spec.js
@@ -19,6 +19,7 @@ import {
   selectFilterOperator,
   entityPickerModal,
   collectionOnTheGoModal,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 describe("scenarios > question > saved", () => {
@@ -80,8 +81,7 @@ describe("scenarios > question > saved", () => {
     cy.findAllByText("Orders"); // question and table name appears
 
     // filter to only orders with quantity=100
-    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.findByText("Quantity").click();
+    tableHeaderClick("Quantity");
     popover().findByText("Filter by this column").click();
     selectFilterOperator("Equal to");
     popover().within(() => {
diff --git a/e2e/test/scenarios/question/settings.cy.spec.js b/e2e/test/scenarios/question/settings.cy.spec.js
index a77714eb8fa..dfe1d961087 100644
--- a/e2e/test/scenarios/question/settings.cy.spec.js
+++ b/e2e/test/scenarios/question/settings.cy.spec.js
@@ -11,6 +11,7 @@ import {
   sidebar,
   moveDnDKitElement,
   entityPickerModal,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 const { ORDERS, ORDERS_ID, PRODUCTS, PRODUCTS_ID } = SAMPLE_DATABASE;
@@ -265,15 +266,23 @@ describe("scenarios > question > settings", () => {
       cy.findByTestId("viz-settings-button").click(); // open settings sidebar
       // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
       cy.findByText("Conditional Formatting"); // confirm it's open
-      cy.get(".test-TableInteractive").findByText("Subtotal").click(); // open subtotal column header actions
+
+      // cy.get(".test-TableInteractive").findByText("Subtotal").scrollIntoView();
+      tableHeaderClick("Subtotal"); // open subtotal column header actions
+
       popover().icon("gear").click(); // open subtotal column settings
 
       //cy.findByText("Table options").should("not.exist"); // no longer displaying the top level settings
       // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
       cy.findByText("Separator style"); // shows subtotal column settings
 
-      cy.get(".test-TableInteractive").findByText("Created At").click(); // open created_at column header actions
-      popover().icon("gear").click(); // open created_at column settings
+      cy.findByTestId("head-crumbs-container").findByText("Orders").click(); //Dismiss popover
+
+      tableHeaderClick("Created At"); // open created_at column header actions
+
+      popover().within(() => {
+        cy.icon("gear").click(); // open created_at column settings
+      });
       // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
       cy.findByText("Date style"); // shows created_at column settings
     });
diff --git a/e2e/test/scenarios/question/summarization.cy.spec.js b/e2e/test/scenarios/question/summarization.cy.spec.js
index 13f1a231998..ff72aba931c 100644
--- a/e2e/test/scenarios/question/summarization.cy.spec.js
+++ b/e2e/test/scenarios/question/summarization.cy.spec.js
@@ -184,7 +184,6 @@ describe("scenarios > question > summarize sidebar", () => {
         formula:
           "sum([Total]) / (sum([Product → Price]) * average([Quantity]))",
       });
-      cy.get("@formula").blur();
     });
 
     popover().within(() => {
diff --git a/e2e/test/scenarios/sharing/downloads/reproductions/19889-native-query-export-column-order.cy.spec.js b/e2e/test/scenarios/sharing/downloads/reproductions/19889-native-query-export-column-order.cy.spec.js
index 5a3e8d9bc3e..d391f4191c8 100644
--- a/e2e/test/scenarios/sharing/downloads/reproductions/19889-native-query-export-column-order.cy.spec.js
+++ b/e2e/test/scenarios/sharing/downloads/reproductions/19889-native-query-export-column-order.cy.spec.js
@@ -23,11 +23,19 @@ describe("issue 19889", () => {
 
     // Reorder columns a and b
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
+    cy.findByText("column a").trigger("mousedown", 0, 0).wait(100); //Don't force the first interaction. This ensures things are actually visible to start moving
+    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
+    cy.findByText("column a")
+      .trigger("mousemove", 10, 10, { force: true })
+      .wait(100);
+    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.findByText("column a")
-      .trigger("mousedown", 0, 0, { force: true })
-      .trigger("mousemove", 5, 5, { force: true })
       .trigger("mousemove", 100, 0, { force: true })
-      .trigger("mouseup", 100, 0, { force: true });
+      .wait(100);
+    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
+    cy.findByText("column a")
+      .trigger("mouseup", 100, 0, { force: true })
+      .wait(100);
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.findByText("Started from").click(); // Give DOM some time to update
   });
diff --git a/e2e/test/scenarios/sharing/public-sharing-embed-button-behavior.cy.spec.js b/e2e/test/scenarios/sharing/public-sharing-embed-button-behavior.cy.spec.js
index 2bb18d886c9..49b4311f128 100644
--- a/e2e/test/scenarios/sharing/public-sharing-embed-button-behavior.cy.spec.js
+++ b/e2e/test/scenarios/sharing/public-sharing-embed-button-behavior.cy.spec.js
@@ -852,7 +852,7 @@ function assertValidPublicLink({ resource, shouldHaveRemoveLink }) {
     cy.findByTestId("public-link-input")
       .should("be.visible")
       .invoke("val")
-      .then(value => {
+      .should(value => {
         expect(value).to.match(regex);
       });
 
diff --git a/e2e/test/scenarios/sharing/reproductions/18344-subscription-shows-original-question-name.cy.spec.js b/e2e/test/scenarios/sharing/reproductions/18344-subscription-shows-original-question-name.cy.spec.js
index bf8cb5e9bb9..de56a4a07ed 100644
--- a/e2e/test/scenarios/sharing/reproductions/18344-subscription-shows-original-question-name.cy.spec.js
+++ b/e2e/test/scenarios/sharing/reproductions/18344-subscription-shows-original-question-name.cy.spec.js
@@ -31,7 +31,7 @@ describe("issue 18344", { tags: "@external" }, () => {
     cy.icon("palette").click();
 
     modal().within(() => {
-      cy.findByDisplayValue("Orders").type("Foo");
+      cy.findByDisplayValue("Orders").type("Foo").blur();
 
       cy.button("Done").click();
     });
diff --git a/e2e/test/scenarios/visualizations-charts/line_chart.cy.spec.js b/e2e/test/scenarios/visualizations-charts/line_chart.cy.spec.js
index dfb0e9dc9b5..00c6517e619 100644
--- a/e2e/test/scenarios/visualizations-charts/line_chart.cy.spec.js
+++ b/e2e/test/scenarios/visualizations-charts/line_chart.cy.spec.js
@@ -609,7 +609,7 @@ describe("scenarios > visualizations > line chart", () => {
       series.forEach(serie => {
         const [old_name, new_name] = serie;
 
-        cy.findByDisplayValue(old_name).clear().type(new_name);
+        cy.findByDisplayValue(old_name).clear().type(new_name).blur();
       });
 
       modal()
@@ -696,6 +696,11 @@ describe("scenarios > visualizations > line chart", () => {
       display: "line",
     });
 
+    queryBuilderMain().within(() => {
+      echartsContainer().findByText("Quantity").should("exist");
+    });
+    cy.wait(100); // wait to avoid grabbing the svg before the chart redraws
+
     cy.findByTestId("query-visualization-root")
       .trigger("mousedown", 180, 200)
       .trigger("mousemove", 180, 200)
diff --git a/e2e/test/scenarios/visualizations-charts/maps.cy.spec.js b/e2e/test/scenarios/visualizations-charts/maps.cy.spec.js
index a25a3594652..398e546f53c 100644
--- a/e2e/test/scenarios/visualizations-charts/maps.cy.spec.js
+++ b/e2e/test/scenarios/visualizations-charts/maps.cy.spec.js
@@ -108,10 +108,9 @@ describe("scenarios > visualizations > maps", () => {
 
       cy.wait("@geojson");
 
-      cy.get(".CardVisualization svg path")
-        .should("be.visible")
-        .eq(22)
-        .as("texas");
+      cy.get(".CardVisualization svg path").eq(22).as("texas");
+
+      cy.get("@texas").should("be.visible");
 
       // hover to see the tooltip
       cy.get("@texas").trigger("mousemove");
diff --git a/e2e/test/scenarios/visualizations-charts/pie_chart.cy.spec.js b/e2e/test/scenarios/visualizations-charts/pie_chart.cy.spec.js
index cfa151f7fbc..86f4bdbf5a1 100644
--- a/e2e/test/scenarios/visualizations-charts/pie_chart.cy.spec.js
+++ b/e2e/test/scenarios/visualizations-charts/pie_chart.cy.spec.js
@@ -1,6 +1,11 @@
 import { SAMPLE_DB_ID } from "e2e/support/cypress_data";
 import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
-import { restore, visitQuestionAdhoc, popover } from "e2e/support/helpers";
+import {
+  restore,
+  visitQuestionAdhoc,
+  popover,
+  tableHeaderClick,
+} from "e2e/support/helpers";
 
 const { PRODUCTS, PRODUCTS_ID } = SAMPLE_DATABASE;
 
@@ -30,7 +35,7 @@ describe("scenarios > visualizations > pie chart", () => {
 
     cy.log("#35244");
     cy.findByLabelText("Switch to data").click();
-    cy.findAllByTestId("header-cell").contains("Count").click();
+    tableHeaderClick("Count");
     popover().within(() => {
       cy.findByRole("img", { name: /filter/ }).should("exist");
       cy.findByRole("img", { name: /gear/ }).should("not.exist");
diff --git a/e2e/test/scenarios/visualizations-charts/reproductions/18063-maps-null-location-wrong-tooltip.cy.spec.js b/e2e/test/scenarios/visualizations-charts/reproductions/18063-maps-null-location-wrong-tooltip.cy.spec.js
index 9e00fa4dd79..622680b73f1 100644
--- a/e2e/test/scenarios/visualizations-charts/reproductions/18063-maps-null-location-wrong-tooltip.cy.spec.js
+++ b/e2e/test/scenarios/visualizations-charts/reproductions/18063-maps-null-location-wrong-tooltip.cy.spec.js
@@ -23,10 +23,10 @@ describe("issue 18063", () => {
 
     popover().contains("Pin map").click();
 
-    // Click anywhere to close both popovers that open automatically.
+    // Click anywhere to close both popovers that open automatically. Need to click twice to dismiss both popovers
     // Please see: https://github.com/metabase/metabase/issues/18063#issuecomment-927836691
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.findByText("Latitude field").click();
+    cy.findByText("New question").click().click();
   });
 
   it("should show the correct tooltip details for pin map even when some locations are null (metabase#18063)", () => {
@@ -51,5 +51,5 @@ function selectFieldValue(field, value) {
       cy.findByText("Select a field").click();
     });
 
-  popover().contains(value).click();
+  popover().findByText(value).click();
 }
diff --git a/e2e/test/scenarios/visualizations-tabular/column-shortcuts/column-shortcuts.cy.spec.ts b/e2e/test/scenarios/visualizations-tabular/column-shortcuts/column-shortcuts.cy.spec.ts
index 128b716b45f..ab87cf66bb0 100644
--- a/e2e/test/scenarios/visualizations-tabular/column-shortcuts/column-shortcuts.cy.spec.ts
+++ b/e2e/test/scenarios/visualizations-tabular/column-shortcuts/column-shortcuts.cy.spec.ts
@@ -15,6 +15,7 @@ import {
   expectGoodSnowplowEvent,
   resetSnowplow,
   expectNoBadSnowplowEvents,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 const { PEOPLE, PEOPLE_ID, ORDERS, ORDERS_ID } = SAMPLE_DATABASE;
@@ -254,9 +255,7 @@ describeWithSnowplow("extract shortcut", () => {
 
     cy.get("#main-data-grid").scrollTo("left", { duration: 2000 / 60 });
 
-    cy.findAllByRole("columnheader", { name: "ID" })
-      .should("be.visible")
-      .click();
+    tableHeaderClick("ID");
 
     // Change sort direction
     popover().findAllByRole("button").first().click();
diff --git a/e2e/test/scenarios/visualizations-tabular/drillthroughs/chart_drill.cy.spec.js b/e2e/test/scenarios/visualizations-tabular/drillthroughs/chart_drill.cy.spec.js
index 9daf970c383..7f26c9fb503 100644
--- a/e2e/test/scenarios/visualizations-tabular/drillthroughs/chart_drill.cy.spec.js
+++ b/e2e/test/scenarios/visualizations-tabular/drillthroughs/chart_drill.cy.spec.js
@@ -19,6 +19,7 @@ import {
   cartesianChartCircleWithColor,
   entityPickerModal,
   entityPickerModalTab,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 const { ORDERS, ORDERS_ID, PRODUCTS, PRODUCTS_ID, PEOPLE, PEOPLE_ID } =
@@ -103,6 +104,12 @@ describe("scenarios > visualizations > drillthroughs > chart drill", () => {
 
       cy.createQuestion(questionDetails, { visitQuestion: true });
 
+      queryBuilderMain().within(() => {
+        cy.findByLabelText("Legend").findByText("Gadget").should("exist");
+        echartsContainer().findByText(/Count/).should("exist");
+      });
+      cy.wait(100); // wait to avoid grabbing the svg before the chart redraws
+
       cy.findByTestId("query-visualization-root")
         .trigger("mousedown", 240, 200)
         .trigger("mousemove", 420, 200)
@@ -555,8 +562,7 @@ describe("scenarios > visualizations > drillthroughs > chart drill", () => {
     cy.findByText("See these Orders").click();
 
     // count number of distinct values in the Discount column
-    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.findByText("Discount ($)").click();
+    tableHeaderClick("Discount ($)");
     // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
     cy.findByText("Distinct values").click();
 
diff --git a/e2e/test/scenarios/visualizations-tabular/drillthroughs/column_extract_drill.cy.spec.js b/e2e/test/scenarios/visualizations-tabular/drillthroughs/column_extract_drill.cy.spec.js
index ca8ef60892f..9617ad422e7 100644
--- a/e2e/test/scenarios/visualizations-tabular/drillthroughs/column_extract_drill.cy.spec.js
+++ b/e2e/test/scenarios/visualizations-tabular/drillthroughs/column_extract_drill.cy.spec.js
@@ -15,6 +15,7 @@ import {
   popover,
   resetSnowplow,
   restore,
+  tableHeaderClick,
   visitQuestion,
   visualize,
 } from "e2e/support/helpers";
@@ -313,7 +314,8 @@ function extractColumnAndCheck({
 }) {
   const requestAlias = _.uniqueId("dataset");
   cy.intercept("POST", "/api/dataset").as(requestAlias);
-  cy.findByRole("columnheader", { name: column }).click();
+  tableHeaderClick(column);
+  // cy.findByRole("columnheader", { name: column }).click();
   popover().findByText(extraction).click();
   cy.wait(1);
 
diff --git a/e2e/test/scenarios/visualizations-tabular/drillthroughs/combine-column.cy.spec.js b/e2e/test/scenarios/visualizations-tabular/drillthroughs/combine-column.cy.spec.js
index fced9452592..8dbf355ce6c 100644
--- a/e2e/test/scenarios/visualizations-tabular/drillthroughs/combine-column.cy.spec.js
+++ b/e2e/test/scenarios/visualizations-tabular/drillthroughs/combine-column.cy.spec.js
@@ -8,6 +8,7 @@ import {
   popover,
   resetSnowplow,
   restore,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 const { PEOPLE, PEOPLE_ID } = SAMPLE_DATABASE;
@@ -40,7 +41,7 @@ describeWithSnowplow(
         { visitQuestion: true },
       );
 
-      cy.findAllByTestId("header-cell").contains("Email").click();
+      tableHeaderClick("Email");
       popover().findByText("Combine columns").click();
 
       popover().within(() => {
@@ -109,12 +110,12 @@ describeWithSnowplow(
       );
 
       // first combine (email + ID)
-      cy.findAllByTestId("header-cell").contains("Email").click();
+      tableHeaderClick("Email");
       popover().findByText("Combine columns").click();
       popover().findByText("Done").click();
 
       // second combine (email + ID)
-      cy.findAllByTestId("header-cell").contains("Email").click();
+      tableHeaderClick("Email");
       popover().findByText("Combine columns").click();
       popover().findByText("Done").click();
 
diff --git a/e2e/test/scenarios/visualizations-tabular/drillthroughs/table_drills.cy.spec.js b/e2e/test/scenarios/visualizations-tabular/drillthroughs/table_drills.cy.spec.js
index b19cf05466c..6e96dc3547f 100644
--- a/e2e/test/scenarios/visualizations-tabular/drillthroughs/table_drills.cy.spec.js
+++ b/e2e/test/scenarios/visualizations-tabular/drillthroughs/table_drills.cy.spec.js
@@ -1,5 +1,10 @@
 import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
-import { openReviewsTable, popover, restore } from "e2e/support/helpers";
+import {
+  openReviewsTable,
+  popover,
+  restore,
+  tableHeaderClick,
+} from "e2e/support/helpers";
 
 const { REVIEWS, REVIEWS_ID } = SAMPLE_DATABASE;
 
@@ -7,6 +12,7 @@ describe("scenarios > visualizations > drillthroughs > table_drills", () => {
   beforeEach(() => {
     restore();
     cy.signInAsAdmin();
+    cy.viewport(1500, 800);
   });
 
   it("should display proper drills on cell click for unaggregated query", () => {
@@ -53,7 +59,7 @@ describe("scenarios > visualizations > drillthroughs > table_drills", () => {
       cy.findByText("View details").should("be.visible");
     });
 
-    cy.get("[data-testid=cell-data]").contains("ID").click({ force: true });
+    tableHeaderClick("ID");
     popover().within(() => {
       cy.icon("arrow_down").should("be.visible");
       cy.icon("arrow_up").should("be.visible");
@@ -63,7 +69,8 @@ describe("scenarios > visualizations > drillthroughs > table_drills", () => {
       cy.findByText("Distinct values").should("be.visible");
     });
 
-    cy.get("[data-testid=cell-data]").contains("Reviewer").click();
+    //cy.get("[data-testid=cell-data]").contains("Reviewer").click();
+    tableHeaderClick("Reviewer");
     popover().within(() => {
       cy.icon("arrow_down").should("be.visible");
       cy.icon("arrow_up").should("be.visible");
@@ -74,7 +81,8 @@ describe("scenarios > visualizations > drillthroughs > table_drills", () => {
       cy.findByText("Distinct values").should("be.visible");
     });
 
-    cy.get("[data-testid=cell-data]").contains("Rating").click();
+    // cy.get("[data-testid=cell-data]").contains("Rating").click();
+    tableHeaderClick("Rating");
     popover().within(() => {
       cy.icon("arrow_down").should("be.visible");
       cy.icon("arrow_up").should("be.visible");
@@ -124,7 +132,7 @@ describe("scenarios > visualizations > drillthroughs > table_drills", () => {
       cy.findByText("≠").should("be.visible");
     });
 
-    cy.get("[data-testid=cell-data]").contains("Reviewer").click();
+    tableHeaderClick("Reviewer");
     popover().within(() => {
       cy.icon("arrow_down").should("be.visible");
       cy.icon("arrow_up").should("be.visible");
@@ -133,7 +141,7 @@ describe("scenarios > visualizations > drillthroughs > table_drills", () => {
       cy.findByText("Filter by this column").should("be.visible");
     });
 
-    cy.get("[data-testid=cell-data]").contains("Count").click();
+    tableHeaderClick("Count");
     popover().within(() => {
       cy.icon("arrow_down").should("be.visible");
       cy.icon("arrow_up").should("be.visible");
diff --git a/e2e/test/scenarios/visualizations-tabular/object_detail.cy.spec.js b/e2e/test/scenarios/visualizations-tabular/object_detail.cy.spec.js
index 220cd0eb403..61a9d141081 100644
--- a/e2e/test/scenarios/visualizations-tabular/object_detail.cy.spec.js
+++ b/e2e/test/scenarios/visualizations-tabular/object_detail.cy.spec.js
@@ -13,6 +13,7 @@ import {
   visitPublicQuestion,
   visitPublicDashboard,
   createQuestion,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 const {
@@ -163,11 +164,11 @@ describe("scenarios > question > object details", { tags: "@slow" }, () => {
 
     createQuestion(questionDetails, { visitQuestion: true });
 
-    // there should be a hover instead of click
-    // but realHover is flaky
-    cy.get("[data-testid=cell-data]").contains("4966277046676").click();
+    cy.findByRole("gridcell", { name: "4966277046676" }).should("exist"); //Check that cell is rendered before mouseing over
+    cy.findByRole("gridcell", { name: "4966277046676" }).trigger("mouseover");
 
     cy.findByTestId("detail-shortcut")
+      .should("have.attr", "data-show-detail-rowindex", "9") // This ensures that the button is on the correct row before cypress clicks it
       .findByRole("button")
       .should("be.visible")
       .click();
@@ -419,7 +420,7 @@ function getNextObjectDetailButton() {
 
 function changeSorting(columnName, direction) {
   const icon = direction === "asc" ? "arrow_up" : "arrow_down";
-  cy.findByText(columnName).click();
+  tableHeaderClick(columnName);
   popover().within(() => {
     cy.icon(icon).click();
   });
diff --git a/e2e/test/scenarios/visualizations-tabular/pivot_tables.cy.spec.js b/e2e/test/scenarios/visualizations-tabular/pivot_tables.cy.spec.js
index 7466526670b..d5f2ffad341 100644
--- a/e2e/test/scenarios/visualizations-tabular/pivot_tables.cy.spec.js
+++ b/e2e/test/scenarios/visualizations-tabular/pivot_tables.cy.spec.js
@@ -1032,10 +1032,10 @@ describe("scenarios > visualizations > pivot tables", { tags: "@slow" }, () => {
       dragColumnHeader(totalHeaderColHandle, 100);
 
       cy.findByTestId("pivot-table").within(() => {
-        cy.findByText("User → Source").then($headerTextEl => {
+        cy.findByText("User → Source").should($headerTextEl => {
           expect(getCellWidth($headerTextEl)).equal(80); // min width is 80
         });
-        cy.findByText("Row totals").then($headerTextEl => {
+        cy.findByText("Row totals").should($headerTextEl => {
           expect(getCellWidth($headerTextEl)).equal(200);
         });
       });
diff --git a/e2e/test/scenarios/visualizations-tabular/table-column-settings.cy.spec.js b/e2e/test/scenarios/visualizations-tabular/table-column-settings.cy.spec.js
index 671ec4e33ea..0d6e4bf01de 100644
--- a/e2e/test/scenarios/visualizations-tabular/table-column-settings.cy.spec.js
+++ b/e2e/test/scenarios/visualizations-tabular/table-column-settings.cy.spec.js
@@ -1,7 +1,13 @@
 import _ from "underscore";
 
 import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
-import { openNotebook, popover, restore, visualize } from "e2e/support/helpers";
+import {
+  openNotebook,
+  popover,
+  restore,
+  tableHeaderClick,
+  visualize,
+} from "e2e/support/helpers";
 
 const { ORDERS_ID, ORDERS, PRODUCTS_ID, PRODUCTS } = SAMPLE_DATABASE;
 
@@ -350,9 +356,7 @@ describe("scenarios > visualizations > table column settings", () => {
     it("should be able to rename table columns via popover", () => {
       cy.createQuestion(tableQuestion, { visitQuestion: true });
 
-      cy.findByTestId("TableInteractive-root").within(() => {
-        cy.findByText("Product ID").click();
-      });
+      tableHeaderClick("Product ID");
 
       popover().within(() => {
         cy.icon("gear").click();
diff --git a/e2e/test/scenarios/visualizations-tabular/table.cy.spec.js b/e2e/test/scenarios/visualizations-tabular/table.cy.spec.js
index 9213cecfec7..d6eedfb4de1 100644
--- a/e2e/test/scenarios/visualizations-tabular/table.cy.spec.js
+++ b/e2e/test/scenarios/visualizations-tabular/table.cy.spec.js
@@ -22,6 +22,7 @@ import {
   expressionEditorWidget,
   entityPickerModal,
   entityPickerModalTab,
+  tableHeaderClick,
 } from "e2e/support/helpers";
 
 describe("scenarios > visualizations > table", () => {
@@ -52,7 +53,7 @@ describe("scenarios > visualizations > table", () => {
     visualize();
 
     // Rename the first ID column, and make sure the second one is not updated
-    headerCells().findByText("ID").click();
+    tableHeaderClick("ID");
     popover().within(() => {
       cy.findByText("Filter by this column");
       cy.icon("gear").click();
@@ -79,13 +80,16 @@ describe("scenarios > visualizations > table", () => {
 
     cy.get("@total")
       .trigger("mousedown", 0, 0, { force: true })
+      .wait(200)
       .trigger("mousemove", 5, 5, { force: true })
+      .wait(200)
       .trigger("mousemove", -220, 0, { force: true })
+      .wait(200)
       .trigger("mouseup", -220, 0, { force: true });
 
     headerCells().eq(1).should("contain.text", "TOTAL");
 
-    headerCells().contains("QUANTITY").click();
+    tableHeaderClick("QUANTITY");
     popover().icon("eye_crossed_out").click();
 
     headerCells().contains("QUANTITY").should("not.exist");
@@ -94,8 +98,7 @@ describe("scenarios > visualizations > table", () => {
   it("should allow to display any column as link with extrapolated url and text", () => {
     openPeopleTable({ limit: 2 });
 
-    // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
-    cy.findByText("City").click();
+    tableHeaderClick("City");
 
     popover().within(() => {
       cy.icon("gear").click();
@@ -323,7 +326,7 @@ describe("scenarios > visualizations > table", () => {
 
   it("popover should not be scrollable horizontally (metabase#31339)", () => {
     openPeopleTable();
-    headerCells().filter(":contains('Password')").click();
+    tableHeaderClick("Password");
 
     popover().findByText("Filter by this column").click();
     selectFilterOperator("Is");
@@ -423,7 +426,7 @@ describe("scenarios > visualizations > table > conditional formatting", () => {
         .should("contain.text", "is less than 6");
 
       cy.findByRole("button", { name: /add a rule/i }).click();
-      cy.findByRole("button", { name: /choose a column/i }).click();
+      // popover should open automatically
       popover().findByText("Subtotal").click();
       cy.realPress("Escape");
       cy.findByRole("button", { name: /is equal to/i }).click();
@@ -544,7 +547,7 @@ describe("scenarios > visualizations > table > time formatting (#11398)", () =>
     );
 
     // Open the formatting menu
-    cy.findByTestId("field-info-popover").click();
+    tableHeaderClick("CREATION_TIME");
 
     popover().icon("gear").click();
 
diff --git a/enterprise/frontend/src/metabase-enterprise/application_permissions/pages/ApplicationPermissionsPage/ApplicationPermissionsPage.tsx b/enterprise/frontend/src/metabase-enterprise/application_permissions/pages/ApplicationPermissionsPage/ApplicationPermissionsPage.tsx
index 27e8a9306e2..ebff86fbf8d 100644
--- a/enterprise/frontend/src/metabase-enterprise/application_permissions/pages/ApplicationPermissionsPage/ApplicationPermissionsPage.tsx
+++ b/enterprise/frontend/src/metabase-enterprise/application_permissions/pages/ApplicationPermissionsPage/ApplicationPermissionsPage.tsx
@@ -56,7 +56,11 @@ const ApplicationPermissionsPage = ({
   }, [initialize]);
 
   const handlePermissionChange = useCallback(
-    (item: { id: GroupId }, permission, value: ApplicationPermissionsValue) => {
+    (
+      item: { id: GroupId },
+      permission: any,
+      value: ApplicationPermissionsValue,
+    ) => {
       updatePermission({
         groupId: item.id,
         permission,
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/CacheSection.tsx b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/CacheSection.tsx
index ca1b1453074..3cbd458ca89 100644
--- a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/CacheSection.tsx
+++ b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/CacheSection.tsx
@@ -20,7 +20,7 @@ const CacheSection = ({ initialCacheTTL, onSave }: CacheSectionProps) => {
   const [cacheTTL, setCacheTTL] = useState(initialCacheTTL);
 
   const handleChange = useCallback(
-    number => {
+    (number: number | undefined) => {
       setCacheTTL(normalizeCacheTTL(number));
     },
     [setCacheTTL],
diff --git a/frontend/src/metabase-types/api/actions.ts b/frontend/src/metabase-types/api/actions.ts
index f98b2cf908f..9167c04bada 100644
--- a/frontend/src/metabase-types/api/actions.ts
+++ b/frontend/src/metabase-types/api/actions.ts
@@ -164,3 +164,7 @@ export type ActionFormOption = {
 export interface WritebackActionListQuery {
   "model-id"?: CardId;
 }
+
+export interface GetActionRequest {
+  id: number;
+}
diff --git a/frontend/src/metabase/ErrorBoundary.tsx b/frontend/src/metabase/ErrorBoundary.tsx
index 0ec3a7de356..db285f7336d 100644
--- a/frontend/src/metabase/ErrorBoundary.tsx
+++ b/frontend/src/metabase/ErrorBoundary.tsx
@@ -5,11 +5,11 @@ import { SmallGenericError } from "metabase/components/ErrorPages";
 
 // eslint-disable-next-line import/no-default-export -- deprecated usage
 export default class ErrorBoundary extends Component<
-  {
+  React.PropsWithChildren<{
     onError?: (errorInfo: ErrorInfo) => void;
     errorComponent?: ComponentType;
     message?: string;
-  },
+  }>,
   {
     hasError: boolean;
   }
diff --git a/frontend/src/metabase/actions/containers/ActionCreatorModal/ActionCreatorModal.tsx b/frontend/src/metabase/actions/containers/ActionCreatorModal/ActionCreatorModal.tsx
index 713410a024a..e906edf5ba7 100644
--- a/frontend/src/metabase/actions/containers/ActionCreatorModal/ActionCreatorModal.tsx
+++ b/frontend/src/metabase/actions/containers/ActionCreatorModal/ActionCreatorModal.tsx
@@ -5,7 +5,7 @@ import type { Route } from "react-router";
 import { replace } from "react-router-redux";
 import _ from "underscore";
 
-import Actions from "metabase/entities/actions";
+import { skipToken, useGetActionQuery } from "metabase/api";
 import Models from "metabase/entities/questions";
 import * as Urls from "metabase/lib/urls";
 import { setErrorPage } from "metabase/redux/app";
@@ -50,10 +50,9 @@ const mapDispatchToProps = {
 };
 
 function ActionCreatorModal({
-  action,
   model,
   params,
-  loading,
+  loading: isModelLoading,
   route,
   onClose,
   setErrorPage,
@@ -63,9 +62,15 @@ function ActionCreatorModal({
   const modelId = Urls.extractEntityId(params.slug);
   const databaseId = model.databaseId();
 
+  const { isLoading: isActionLoading, data: action } = useGetActionQuery(
+    actionId === undefined ? skipToken : { id: actionId },
+  );
+
+  const loading = isModelLoading || isActionLoading;
+
   useEffect(() => {
     if (loading === false) {
-      const notFound = params.actionId && !action;
+      const notFound = actionId && !action;
       const hasModelMismatch = action != null && action.model_id !== modelId;
 
       if (notFound || action?.archived) {
@@ -94,10 +99,6 @@ function ActionCreatorModal({
   );
 }
 
-function getActionId(state: State, props: OwnProps) {
-  return Urls.extractEntityId(props.params.actionId);
-}
-
 function getModelId(state: State, props: OwnProps) {
   return Urls.extractEntityId(props.params.slug);
 }
@@ -108,6 +109,5 @@ export default _.compose(
     id: getModelId,
     entityAlias: "model",
   }),
-  Actions.load({ id: getActionId, loadingAndErrorWrapper: false }),
   connect(null, mapDispatchToProps),
 )(ActionCreatorModal);
diff --git a/frontend/src/metabase/admin/datamodel/components/filters/pickers/DatePicker/DatePicker.tsx b/frontend/src/metabase/admin/datamodel/components/filters/pickers/DatePicker/DatePicker.tsx
index 4c6840dfb40..303b3b94d6d 100644
--- a/frontend/src/metabase/admin/datamodel/components/filters/pickers/DatePicker/DatePicker.tsx
+++ b/frontend/src/metabase/admin/datamodel/components/filters/pickers/DatePicker/DatePicker.tsx
@@ -143,7 +143,7 @@ type Props = {
   onFilterChange: (filter: any[]) => void;
 };
 
-const DatePicker: React.FC<Props> = props => {
+const DatePicker: React.FC<React.PropsWithChildren<Props>> = props => {
   const {
     className,
     filter,
diff --git a/frontend/src/metabase/admin/datamodel/components/filters/pickers/DatePicker/DatePickerFooter.tsx b/frontend/src/metabase/admin/datamodel/components/filters/pickers/DatePicker/DatePickerFooter.tsx
index 8dd72e220d9..9a31a658b08 100644
--- a/frontend/src/metabase/admin/datamodel/components/filters/pickers/DatePicker/DatePickerFooter.tsx
+++ b/frontend/src/metabase/admin/datamodel/components/filters/pickers/DatePicker/DatePickerFooter.tsx
@@ -35,7 +35,7 @@ const getIntervalString = (filter: Filter) => {
   return start.format(formatString) + " - " + end.format(formatString);
 };
 
-const DatePickerFooter: React.FC<Props> = ({
+const DatePickerFooter: React.FC<React.PropsWithChildren<Props>> = ({
   filter,
   primaryColor,
   onFilterChange,
diff --git a/frontend/src/metabase/admin/datamodel/containers/SegmentApp.jsx b/frontend/src/metabase/admin/datamodel/containers/SegmentApp.jsx
index 488a003b8b2..ab1b3c3ec3f 100644
--- a/frontend/src/metabase/admin/datamodel/containers/SegmentApp.jsx
+++ b/frontend/src/metabase/admin/datamodel/containers/SegmentApp.jsx
@@ -5,6 +5,7 @@ import { push } from "react-router-redux";
 
 import { LeaveConfirmationModal } from "metabase/components/LeaveConfirmationModal";
 import Segments from "metabase/entities/segments";
+import { useCallbackEffect } from "metabase/hooks/use-callback-effect";
 import * as MetabaseAnalytics from "metabase/lib/analytics";
 
 import SegmentForm from "../components/SegmentForm";
@@ -71,22 +72,30 @@ const CreateSegmentForm = ({
 }) => {
   const [isDirty, setIsDirty] = useState(false);
 
+  /**
+   * Navigation is scheduled so that LeaveConfirmationModal's isEnabled
+   * prop has a chance to re-compute on re-render
+   */
+  const [, scheduleCallback] = useCallbackEffect();
+
   const handleSubmit = useCallback(
-    async segment => {
+    segment => {
       setIsDirty(false);
 
-      try {
-        await createSegment({
-          ...segment,
-          table_id: segment.definition["source-table"],
-        });
-        MetabaseAnalytics.trackStructEvent("Data Model", "Segment Updated");
-        onChangeLocation("/admin/datamodel/segments");
-      } catch (error) {
-        setIsDirty(isDirty);
-      }
+      scheduleCallback(async () => {
+        try {
+          await createSegment({
+            ...segment,
+            table_id: segment.definition["source-table"],
+          });
+          MetabaseAnalytics.trackStructEvent("Data Model", "Segment Updated");
+          onChangeLocation("/admin/datamodel/segments");
+        } catch (error) {
+          setIsDirty(isDirty);
+        }
+      });
     },
-    [createSegment, isDirty, onChangeLocation],
+    [scheduleCallback, createSegment, isDirty, onChangeLocation],
   );
 
   return (
diff --git a/frontend/src/metabase/admin/permissions/test/DatabasesPermissionsPage/DatabasesPermissionsPage.unit.spec.tsx b/frontend/src/metabase/admin/permissions/test/DatabasesPermissionsPage/DatabasesPermissionsPage.unit.spec.tsx
index 0f1f21ddf32..9eb6354e554 100644
--- a/frontend/src/metabase/admin/permissions/test/DatabasesPermissionsPage/DatabasesPermissionsPage.unit.spec.tsx
+++ b/frontend/src/metabase/admin/permissions/test/DatabasesPermissionsPage/DatabasesPermissionsPage.unit.spec.tsx
@@ -13,10 +13,10 @@ import {
   screen,
   waitForLoaderToBeRemoved,
 } from "__support__/ui";
+import { delay } from "__support__/utils";
 import DataPermissionsPage from "metabase/admin/permissions/pages/DataPermissionsPage/DataPermissionsPage";
 import DatabasesPermissionsPage from "metabase/admin/permissions/pages/DatabasePermissionsPage/DatabasesPermissionsPage";
 import { BEFORE_UNLOAD_UNSAVED_MESSAGE } from "metabase/hooks/use-before-unload";
-import { delay } from "metabase/lib/promise";
 import { PLUGIN_ADMIN_PERMISSIONS_TABLE_GROUP_ROUTES } from "metabase/plugins";
 import { createMockGroup } from "metabase-types/api/mocks/group";
 import { createSampleDatabase } from "metabase-types/api/mocks/presets";
diff --git a/frontend/src/metabase/admin/permissions/test/GroupsPermissionsPage/GroupsPermissionsPage.unit.spec.tsx b/frontend/src/metabase/admin/permissions/test/GroupsPermissionsPage/GroupsPermissionsPage.unit.spec.tsx
index 759a41e7514..fc9183106d0 100644
--- a/frontend/src/metabase/admin/permissions/test/GroupsPermissionsPage/GroupsPermissionsPage.unit.spec.tsx
+++ b/frontend/src/metabase/admin/permissions/test/GroupsPermissionsPage/GroupsPermissionsPage.unit.spec.tsx
@@ -13,10 +13,10 @@ import {
   screen,
   waitForLoaderToBeRemoved,
 } from "__support__/ui";
+import { delay } from "__support__/utils";
 import DataPermissionsPage from "metabase/admin/permissions/pages/DataPermissionsPage/DataPermissionsPage";
 import GroupsPermissionsPage from "metabase/admin/permissions/pages/GroupDataPermissionsPage/GroupsPermissionsPage";
 import { BEFORE_UNLOAD_UNSAVED_MESSAGE } from "metabase/hooks/use-before-unload";
-import { delay } from "metabase/lib/promise";
 import { PLUGIN_ADMIN_PERMISSIONS_TABLE_ROUTES } from "metabase/plugins";
 import { createMockGroup } from "metabase-types/api/mocks/group";
 import { createSampleDatabase } from "metabase-types/api/mocks/presets";
diff --git a/frontend/src/metabase/admin/settings/components/Email/SMTPConnectionForm.tsx b/frontend/src/metabase/admin/settings/components/Email/SMTPConnectionForm.tsx
index ff832e89cc4..04f09f2110e 100644
--- a/frontend/src/metabase/admin/settings/components/Email/SMTPConnectionForm.tsx
+++ b/frontend/src/metabase/admin/settings/components/Email/SMTPConnectionForm.tsx
@@ -94,7 +94,7 @@ export const SMTPConnectionForm = ({
   }, [dispatch]);
 
   const handleUpdateEmailSettings = useCallback(
-    async formData => {
+    async (formData: object) => {
       await dispatch(updateEmailSettings(formData));
 
       if (!isEmailConfigured) {
@@ -195,10 +195,10 @@ export const SMTPConnectionForm = ({
                 <Group>
                   {Object.entries(
                     elementMap["email-smtp-security"].options || {},
-                  ).map(([value, name]) => (
+                  ).map(([value, setting]) => (
                     <Radio
                       value={value}
-                      label={name}
+                      label={setting.name}
                       key={value}
                       styles={{
                         inner: { display: "none" },
diff --git a/frontend/src/metabase/api/action.ts b/frontend/src/metabase/api/action.ts
new file mode 100644
index 00000000000..57a2ec3d65d
--- /dev/null
+++ b/frontend/src/metabase/api/action.ts
@@ -0,0 +1,18 @@
+import type { GetActionRequest, WritebackAction } from "metabase-types/api";
+
+import { Api } from "./api";
+import { idTag } from "./tags";
+
+export const actionApi = Api.injectEndpoints({
+  endpoints: builder => ({
+    getAction: builder.query<WritebackAction, GetActionRequest>({
+      query: ({ id }) => ({
+        method: "GET",
+        url: `/api/action/${id}`,
+      }),
+      providesTags: action => (action ? [idTag("action", action.id)] : []),
+    }),
+  }),
+});
+
+export const { useGetActionQuery } = actionApi;
diff --git a/frontend/src/metabase/api/index.ts b/frontend/src/metabase/api/index.ts
index a9e5de70830..720b90f0cdb 100644
--- a/frontend/src/metabase/api/index.ts
+++ b/frontend/src/metabase/api/index.ts
@@ -1,3 +1,4 @@
+export * from "./action";
 export * from "./activity";
 export * from "./alert";
 export * from "./api";
diff --git a/frontend/src/metabase/app.js b/frontend/src/metabase/app.js
index 9724c7ae7bf..763025917e8 100644
--- a/frontend/src/metabase/app.js
+++ b/frontend/src/metabase/app.js
@@ -27,7 +27,7 @@ import "ee-plugins"; // eslint-disable-line import/no-duplicates
 import { createHistory } from "history";
 import { DragDropContextProvider } from "react-dnd";
 import HTML5Backend from "react-dnd-html5-backend";
-import ReactDOM from "react-dom";
+import { createRoot } from "react-dom/client";
 import { Provider } from "react-redux";
 import { Router, useRouterHistory } from "react-router";
 import { syncHistoryWithStore } from "react-router-redux";
@@ -65,7 +65,9 @@ function _init(reducers, getRoutes, callback) {
 
   initializeEmbedding(store);
 
-  ReactDOM.render(
+  const root = createRoot(document.getElementById("root"));
+
+  root.render(
     <Provider store={store}>
       <EmotionCacheProvider>
         <DragDropContextProvider backend={HTML5Backend} context={{ window }}>
@@ -76,7 +78,6 @@ function _init(reducers, getRoutes, callback) {
         </DragDropContextProvider>
       </EmotionCacheProvider>
     </Provider>,
-    document.getElementById("root"),
   );
 
   registerVisualizations();
diff --git a/frontend/src/metabase/auth/actions.ts b/frontend/src/metabase/auth/actions.ts
index 6f1dbfaa6b4..4d37e3aa4c0 100644
--- a/frontend/src/metabase/auth/actions.ts
+++ b/frontend/src/metabase/auth/actions.ts
@@ -38,7 +38,7 @@ export const refreshSession = createAsyncThunk(
   async (_, { dispatch }) => {
     await Promise.all([
       dispatch(refreshCurrentUser()),
-      dispatch(refreshSiteSettings()),
+      dispatch(refreshSiteSettings({})),
     ]);
     await dispatch(refreshLocale()).unwrap();
   },
@@ -52,15 +52,11 @@ interface LoginPayload {
 export const LOGIN = "metabase/auth/LOGIN";
 export const login = createAsyncThunk(
   LOGIN,
-  async (
-    { data, redirectUrl = "/" }: LoginPayload,
-    { dispatch, rejectWithValue },
-  ) => {
+  async ({ data }: LoginPayload, { dispatch, rejectWithValue }) => {
     try {
       await SessionApi.create(data);
       await dispatch(refreshSession()).unwrap();
       trackLogin();
-      dispatch(push(redirectUrl));
       if (!isSmallScreen()) {
         dispatch(openNavbar());
       }
@@ -78,15 +74,11 @@ interface LoginGooglePayload {
 export const LOGIN_GOOGLE = "metabase/auth/LOGIN_GOOGLE";
 export const loginGoogle = createAsyncThunk(
   LOGIN_GOOGLE,
-  async (
-    { credential, redirectUrl = "/" }: LoginGooglePayload,
-    { dispatch, rejectWithValue },
-  ) => {
+  async ({ credential }: LoginGooglePayload, { dispatch, rejectWithValue }) => {
     try {
       await SessionApi.createWithGoogleAuth({ token: credential });
       await dispatch(refreshSession()).unwrap();
       trackLoginGoogle();
-      dispatch(push(redirectUrl));
       if (!isSmallScreen()) {
         dispatch(openNavbar());
       }
diff --git a/frontend/src/metabase/collections/components/CollectionContent/CollectionContent.tsx b/frontend/src/metabase/collections/components/CollectionContent/CollectionContent.tsx
index b2f5192999a..3d1e5c1cbda 100644
--- a/frontend/src/metabase/collections/components/CollectionContent/CollectionContent.tsx
+++ b/frontend/src/metabase/collections/components/CollectionContent/CollectionContent.tsx
@@ -1,3 +1,5 @@
+import { useCallback } from "react";
+
 import {
   useBookmarkListQuery,
   useCollectionListQuery,
@@ -64,16 +66,13 @@ export function CollectionContent({
   const deleteBookmark = (id: BookmarkId, type: BookmarkType) =>
     dispatch(Bookmark.actions.delete({ id, type }));
 
-  const uploadFile = ({
-    file,
-    modelId,
-    collectionId,
-    tableId,
-    uploadMode,
-  }: UploadFileProps) =>
-    dispatch(
-      uploadFileAction({ file, modelId, collectionId, tableId, uploadMode }),
-    );
+  const uploadFile = useCallback(
+    ({ file, modelId, collectionId, tableId, uploadMode }: UploadFileProps) =>
+      dispatch(
+        uploadFileAction({ file, modelId, collectionId, tableId, uploadMode }),
+      ),
+    [dispatch],
+  );
 
   const error =
     bookmarksError || databasesError || collectionsError || collectionError;
diff --git a/frontend/src/metabase/common/components/EntityPicker/components/NestedItemPicker/NestedItemPicker.styled.tsx b/frontend/src/metabase/common/components/EntityPicker/components/NestedItemPicker/NestedItemPicker.styled.tsx
index d4d7f6e4eb3..849ffcdda2f 100644
--- a/frontend/src/metabase/common/components/EntityPicker/components/NestedItemPicker/NestedItemPicker.styled.tsx
+++ b/frontend/src/metabase/common/components/EntityPicker/components/NestedItemPicker/NestedItemPicker.styled.tsx
@@ -3,7 +3,7 @@ import styled from "@emotion/styled";
 import { color } from "metabase/lib/colors";
 import { Box } from "metabase/ui";
 
-export const ListBox = styled(Box)`
+export const ListBox = styled(Box)<React.PropsWithChildren>`
   border-right: 1px solid ${color("border")};
   height: 100%;
   width: 365px;
diff --git a/frontend/src/metabase/components/tree/Tree.tsx b/frontend/src/metabase/components/tree/Tree.tsx
index e2181a9314a..e7a04f644ea 100644
--- a/frontend/src/metabase/components/tree/Tree.tsx
+++ b/frontend/src/metabase/components/tree/Tree.tsx
@@ -5,7 +5,7 @@ import _ from "underscore";
 
 import { TreeNode as DefaultTreeNode } from "./TreeNode";
 import { TreeNodeList } from "./TreeNodeList";
-import type { ITreeNodeItem, TreeNodeComponent } from "./types";
+import type { ITreeNodeItem } from "./types";
 import { getInitialExpandedIds } from "./utils";
 
 interface TreeProps {
@@ -14,7 +14,7 @@ interface TreeProps {
   role?: string;
   emptyState?: React.ReactNode;
   onSelect?: (item: ITreeNodeItem) => void;
-  TreeNode?: TreeNodeComponent;
+  TreeNode?: any; // This was previously set to TreeNodeComponent, but after upgrading to react 18, the type no longer played nice with forward ref compontents, including styled components
 }
 
 function BaseTree({
diff --git a/frontend/src/metabase/components/tree/TreeNode.tsx b/frontend/src/metabase/components/tree/TreeNode.tsx
index f5f7a055492..63b8dcc75a3 100644
--- a/frontend/src/metabase/components/tree/TreeNode.tsx
+++ b/frontend/src/metabase/components/tree/TreeNode.tsx
@@ -24,7 +24,7 @@ const BaseTreeNode = React.memo(
       onSelect,
       onToggleExpand,
       ...props
-    },
+    }: TreeNodeProps,
     ref,
   ) {
     const { name, icon } = item;
diff --git a/frontend/src/metabase/components/tree/types.ts b/frontend/src/metabase/components/tree/types.ts
index 486a64b7de1..10f7ee3af04 100644
--- a/frontend/src/metabase/components/tree/types.ts
+++ b/frontend/src/metabase/components/tree/types.ts
@@ -20,5 +20,5 @@ export interface TreeNodeProps {
 }
 
 export type TreeNodeComponent = React.ComponentType<
-  TreeNodeProps & React.RefAttributes<HTMLLIElement>
+  React.PropsWithChildren<TreeNodeProps & React.RefAttributes<HTMLLIElement>>
 >;
diff --git a/frontend/src/metabase/containers/AdHocQuestionLoader.unit.spec.js b/frontend/src/metabase/containers/AdHocQuestionLoader.unit.spec.js
index 05dd625d2a3..5845595e727 100644
--- a/frontend/src/metabase/containers/AdHocQuestionLoader.unit.spec.js
+++ b/frontend/src/metabase/containers/AdHocQuestionLoader.unit.spec.js
@@ -1,7 +1,7 @@
 import { render } from "@testing-library/react";
 
+import { delay } from "__support__/utils";
 import { AdHocQuestionLoader } from "metabase/containers/AdHocQuestionLoader";
-import { delay } from "metabase/lib/promise";
 import Question from "metabase-lib/v1/Question";
 import * as ML_Urls from "metabase-lib/v1/urls";
 
diff --git a/frontend/src/metabase/dashboard/components/ClickBehaviorSidebar/TableClickBehaviorView/TableClickBehaviorView.tsx b/frontend/src/metabase/dashboard/components/ClickBehaviorSidebar/TableClickBehaviorView/TableClickBehaviorView.tsx
index 4853634e337..45b685f3ef3 100644
--- a/frontend/src/metabase/dashboard/components/ClickBehaviorSidebar/TableClickBehaviorView/TableClickBehaviorView.tsx
+++ b/frontend/src/metabase/dashboard/components/ClickBehaviorSidebar/TableClickBehaviorView/TableClickBehaviorView.tsx
@@ -24,7 +24,7 @@ type ColumnGroup = [
   ClickBehaviorType,
   {
     column: DatasetColumn;
-    clickBehavior: ClickBehavior | undefined;
+    clickBehavior: ClickBehavior;
   }[],
 ];
 
@@ -76,7 +76,13 @@ export function TableClickBehaviorView({
   }, [columns, getClickBehaviorForColumn]) as unknown as ColumnGroup[]; // _.groupby swallows the ClickAction type
 
   const renderColumn = useCallback(
-    ({ column, clickBehavior }, index: number) => {
+    (
+      {
+        column,
+        clickBehavior,
+      }: { column: DatasetColumn; clickBehavior: ClickBehavior },
+      index: number,
+    ) => {
       return (
         <Column
           key={index}
diff --git a/frontend/src/metabase/dashboard/components/DashboardGrid.tsx b/frontend/src/metabase/dashboard/components/DashboardGrid.tsx
index f8efb0dc05b..1608b476449 100644
--- a/frontend/src/metabase/dashboard/components/DashboardGrid.tsx
+++ b/frontend/src/metabase/dashboard/components/DashboardGrid.tsx
@@ -348,7 +348,7 @@ class DashboardGrid extends Component<DashboardGridProps, DashboardGridState> {
   getRowHeight() {
     const { width } = this.props;
 
-    const contentViewportElement = this.context;
+    const contentViewportElement = this.context as any;
     const hasScroll =
       contentViewportElement?.clientHeight <
       contentViewportElement?.scrollHeight;
diff --git a/frontend/src/metabase/hoc/Favicon.jsx b/frontend/src/metabase/hoc/Favicon.jsx
index 3d00820dbf5..8fcc383cc1f 100644
--- a/frontend/src/metabase/hoc/Favicon.jsx
+++ b/frontend/src/metabase/hoc/Favicon.jsx
@@ -26,11 +26,11 @@ const withFavicon = faviconSetterOrGetter => ComposedComponent => {
     const favicon = resolveFavicon(faviconSetterOrGetter, props);
 
     useEffect(() => {
-      document.querySelector('link[rel="icon"]').setAttribute("href", favicon);
+      document.querySelector('link[rel="icon"]')?.setAttribute("href", favicon);
       return () => {
         document
           .querySelector('link[rel="icon"]')
-          .setAttribute("href", DEFAULT_FAVICON());
+          ?.setAttribute("href", DEFAULT_FAVICON());
       };
     }, [favicon]);
     return <ComposedComponent {...props} />;
diff --git a/frontend/src/metabase/lib/formatting/value.tsx b/frontend/src/metabase/lib/formatting/value.tsx
index 0584672d568..a3d293569a5 100644
--- a/frontend/src/metabase/lib/formatting/value.tsx
+++ b/frontend/src/metabase/lib/formatting/value.tsx
@@ -130,7 +130,7 @@ function formatStringFallback(value: any, options: OptionsType = {}) {
 export function formatValueRaw(
   value: unknown,
   options: OptionsType = {},
-): React.ReactElement | Moment | string | number | null {
+): React.ReactElement | string | number | null {
   options = {
     jsx: false,
     remap: true,
diff --git a/frontend/src/metabase/lib/redux/utils.unit.spec.js b/frontend/src/metabase/lib/redux/utils.unit.spec.js
index 27b271925d1..dcef1040da5 100644
--- a/frontend/src/metabase/lib/redux/utils.unit.spec.js
+++ b/frontend/src/metabase/lib/redux/utils.unit.spec.js
@@ -1,4 +1,4 @@
-import { delay } from "metabase/lib/promise";
+import { delay } from "__support__/utils";
 
 import { fetchData, updateData, mergeEntities } from "./utils";
 
diff --git a/frontend/src/metabase/models/containers/ModelDetailPage/ModelDetailPage.tsx b/frontend/src/metabase/models/containers/ModelDetailPage/ModelDetailPage.tsx
index 2b0f1fd0dc2..16a9d67ddd3 100644
--- a/frontend/src/metabase/models/containers/ModelDetailPage/ModelDetailPage.tsx
+++ b/frontend/src/metabase/models/containers/ModelDetailPage/ModelDetailPage.tsx
@@ -155,7 +155,7 @@ function ModelDetailPage({
   );
 
   const handleDescriptionChange = useCallback(
-    description => {
+    (description?: string | null) => {
       if (model.description() !== description) {
         const nextCard = model.setDescription(description).card();
         onChangeModel(nextCard as Card);
diff --git a/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/MainNavbarContainer.tsx b/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/MainNavbarContainer.tsx
index 576ca69ae6f..44327aa1a8e 100644
--- a/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/MainNavbarContainer.tsx
+++ b/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/MainNavbarContainer.tsx
@@ -132,7 +132,7 @@ function MainNavbarContainer({
   }, [rootCollection, trashCollection, collections, currentUser]);
 
   const reorderBookmarks = useCallback(
-    ({ newIndex, oldIndex }) => {
+    ({ newIndex, oldIndex }: { newIndex: number; oldIndex: number }) => {
       const newBookmarks = [...bookmarks];
       const movedBookmark = newBookmarks[oldIndex];
 
diff --git a/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/MainNavbarView.tsx b/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/MainNavbarView.tsx
index 892ce20fcc3..711736c59ad 100644
--- a/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/MainNavbarView.tsx
+++ b/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/MainNavbarView.tsx
@@ -197,7 +197,7 @@ function CollectionSectionHeading({
   handleCreateNewCollection,
 }: CollectionSectionHeadingProps) {
   const renderMenu = useCallback(
-    ({ closePopover }) => (
+    ({ closePopover }: { closePopover: () => void }) => (
       <CollectionMenuList>
         <SidebarLink
           icon="add"
diff --git a/frontend/src/metabase/public/components/PublicError.tsx b/frontend/src/metabase/public/components/PublicError.tsx
index 43cb267b65f..cba69d63ac2 100644
--- a/frontend/src/metabase/public/components/PublicError.tsx
+++ b/frontend/src/metabase/public/components/PublicError.tsx
@@ -17,7 +17,7 @@ export const PublicError = () => {
       >
         <NoDataError mb="1rem" />
         <div className={cx(CS.mt1, CS.h4, CS.smH3, CS.mdH2, CS.textBold)}>
-          {message}
+          {message.toString()}
         </div>
       </div>
     </SyncedEmbedFrame>
diff --git a/frontend/src/metabase/query_builder/components/DataSelector/DataSelector.styled.tsx b/frontend/src/metabase/query_builder/components/DataSelector/DataSelector.styled.tsx
index 302840560e3..09a840e2add 100644
--- a/frontend/src/metabase/query_builder/components/DataSelector/DataSelector.styled.tsx
+++ b/frontend/src/metabase/query_builder/components/DataSelector/DataSelector.styled.tsx
@@ -85,7 +85,7 @@ export const TriggerContainerIcon = styled.div`
   height: 100%;
 `;
 
-export const TextSchema = styled(Text)`
+export const TextSchema = styled(Text)<React.PropsWithChildren>`
   font-size: 0.75em;
   color: ${color("text-light")};
   line-height: 0.75rem;
diff --git a/frontend/src/metabase/query_builder/components/DataSelector/DataSelector.unit.spec.js b/frontend/src/metabase/query_builder/components/DataSelector/DataSelector.unit.spec.js
index 316f1e5b1c7..42acdff8e7c 100644
--- a/frontend/src/metabase/query_builder/components/DataSelector/DataSelector.unit.spec.js
+++ b/frontend/src/metabase/query_builder/components/DataSelector/DataSelector.unit.spec.js
@@ -3,7 +3,7 @@ import fetchMock from "fetch-mock";
 
 import { createMockMetadata } from "__support__/metadata";
 import { getIcon, render, renderWithProviders, screen } from "__support__/ui";
-import { delay } from "metabase/lib/promise";
+import { delay } from "__support__/utils";
 import { UnconnectedDataSelector as DataSelector } from "metabase/query_builder/components/DataSelector";
 import {
   createMockDatabase,
diff --git a/frontend/src/metabase/query_builder/components/notebook/NotebookNativePreview/NotebookNativePreview.tsx b/frontend/src/metabase/query_builder/components/notebook/NotebookNativePreview/NotebookNativePreview.tsx
index bed4ae2282e..1e3fb9936b0 100644
--- a/frontend/src/metabase/query_builder/components/notebook/NotebookNativePreview/NotebookNativePreview.tsx
+++ b/frontend/src/metabase/query_builder/components/notebook/NotebookNativePreview/NotebookNativePreview.tsx
@@ -39,7 +39,7 @@ export const NotebookNativePreview = (): JSX.Element => {
   const { data, error, isFetching } = useGetNativeDatasetQuery(payload);
 
   const showLoader = isFetching;
-  const showError = !isFetching && canRun && error;
+  const showError = !isFetching && canRun && !!error;
   const showQuery = !isFetching && canRun && !error;
   const showEmptySidebar = !canRun;
 
diff --git a/frontend/src/metabase/query_builder/components/view/View/NotebookContainer/NotebookContainer.tsx b/frontend/src/metabase/query_builder/components/view/View/NotebookContainer/NotebookContainer.tsx
index e177befb6a1..643ce05f41e 100644
--- a/frontend/src/metabase/query_builder/components/view/View/NotebookContainer/NotebookContainer.tsx
+++ b/frontend/src/metabase/query_builder/components/view/View/NotebookContainer/NotebookContainer.tsx
@@ -4,7 +4,7 @@ import type { ResizeCallbackData, ResizableBoxProps } from "react-resizable";
 import { ResizableBox } from "react-resizable";
 import { useWindowSize } from "react-use";
 
-import { color } from "metabase/lib/colors";
+import { color, darken } from "metabase/lib/colors";
 import { useSelector, useDispatch } from "metabase/lib/redux";
 import {
   setNotebookNativePreviewSidebarWidth,
@@ -64,31 +64,35 @@ export const NotebookContainer = ({
 
   const transformStyle = isOpen ? "translateY(0)" : "translateY(-100%)";
 
-  const Handle = forwardRef<HTMLDivElement, Partial<ResizableBoxProps>>(
-    function Handle(props, ref) {
-      const handleWidth = 10;
-      const borderWidth = 1;
-      const left = rem(-((handleWidth + borderWidth) / 2));
-
-      return (
-        <Box
-          data-testid="notebook-native-preview-resize-handle"
-          ref={ref}
-          {...props}
-          pos="absolute"
-          top={0}
-          bottom={0}
-          m="auto 0"
-          w={rem(handleWidth)}
-          left={left}
-          style={{
-            zIndex: 5,
-            cursor: "ew-resize",
-          }}
-        ></Box>
-      );
-    },
-  );
+  const Handle = forwardRef<
+    HTMLDivElement,
+    Partial<ResizableBoxProps> & { onResize?: any } //Mantine and react-resizeable have different opinions on what onResize should be
+  >(function Handle(props, ref) {
+    const handleWidth = 10;
+    const borderWidth = 1;
+    const left = rem(-((handleWidth + borderWidth) / 2));
+
+    return (
+      <Box
+        data-testid="notebook-native-preview-resize-handle"
+        ref={ref}
+        {...props}
+        pos="absolute"
+        top={0}
+        bottom={0}
+        m="auto 0"
+        h={rem(100)}
+        w={rem(handleWidth)}
+        left={left}
+        bg={darken("border", 0.03)}
+        style={{
+          zIndex: 5,
+          cursor: "ew-resize",
+          borderRadius: rem(8),
+        }}
+      ></Box>
+    );
+  });
 
   return (
     <Flex
diff --git a/frontend/src/metabase/route-guards.tsx b/frontend/src/metabase/route-guards.tsx
index 065af01e936..6b789a493b2 100644
--- a/frontend/src/metabase/route-guards.tsx
+++ b/frontend/src/metabase/route-guards.tsx
@@ -3,11 +3,21 @@ import { connectedReduxRedirect } from "redux-auth-wrapper/history3/redirect";
 
 import { getAdminPaths } from "metabase/admin/app/selectors";
 import { getIsMetabotEnabled } from "metabase/home/selectors";
+import { isSameOrSiteUrlOrigin } from "metabase/lib/dom";
 import { getSetting } from "metabase/selectors/settings";
 import type { State } from "metabase-types/store";
 
 type Props = { children: React.ReactElement };
 
+const getRedirectUrl = () => {
+  const params = new URLSearchParams(window.location.search);
+  const redirectUrlParam = params.get("redirect");
+
+  return redirectUrlParam != null && isSameOrSiteUrlOrigin(redirectUrlParam)
+    ? redirectUrlParam
+    : "/";
+};
+
 const MetabaseIsSetup = connectedReduxRedirect<Props, State>({
   // eslint-disable-next-line no-literal-metabase-strings -- Not a user facing string
   wrapperDisplayName: "MetabaseIsSetup",
@@ -35,7 +45,7 @@ const UserIsAdmin = connectedReduxRedirect<Props, State>({
 
 const UserIsNotAuthenticated = connectedReduxRedirect<Props, State>({
   wrapperDisplayName: "UserIsNotAuthenticated",
-  redirectPath: "/",
+  redirectPath: () => getRedirectUrl(),
   allowRedirectBack: false,
   authenticatingSelector: state => state.auth.loginPending,
   authenticatedSelector: state => !state.currentUser,
diff --git a/frontend/src/metabase/search/components/SearchResult/SearchResult.styled.tsx b/frontend/src/metabase/search/components/SearchResult/SearchResult.styled.tsx
index 447200d6af0..26014d7c924 100644
--- a/frontend/src/metabase/search/components/SearchResult/SearchResult.styled.tsx
+++ b/frontend/src/metabase/search/components/SearchResult/SearchResult.styled.tsx
@@ -1,7 +1,12 @@
 import isPropValid from "@emotion/is-prop-valid";
 import { css } from "@emotion/react";
 import styled from "@emotion/styled";
-import type { AnchorHTMLAttributes, HTMLAttributes, RefObject } from "react";
+import type {
+  AnchorHTMLAttributes,
+  HTMLAttributes,
+  RefObject,
+  PropsWithChildren,
+} from "react";
 
 import Markdown from "metabase/core/components/Markdown";
 import { PLUGIN_MODERATION } from "metabase/plugins";
@@ -104,7 +109,7 @@ export const XRayButton = styled(Button)<
   height: 2rem;
 `;
 
-export const DescriptionSection = styled(Box)`
+export const DescriptionSection = styled(Box)<PropsWithChildren>`
   margin-top: 0.5rem;
 `;
 
diff --git a/frontend/src/metabase/static-viz/index.js b/frontend/src/metabase/static-viz/index.js
index 356713e5ff4..6987169ab82 100644
--- a/frontend/src/metabase/static-viz/index.js
+++ b/frontend/src/metabase/static-viz/index.js
@@ -1,3 +1,5 @@
+import "fast-text-encoding";
+
 import { setPlatformAPI } from "echarts/core";
 import ReactDOMServer from "react-dom/server";
 import "metabase/lib/dayjs";
diff --git a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetailHeader.tsx b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetailHeader.tsx
index d7db0f40530..b89525016ce 100644
--- a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetailHeader.tsx
+++ b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetailHeader.tsx
@@ -18,7 +18,7 @@ export interface ObjectDetailHeaderProps {
   }[];
   canZoom: boolean;
   objectName: string;
-  objectId: ObjectId | null | unknown;
+  objectId: ObjectId | null;
   canZoomPreviousRow: boolean;
   canZoomNextRow?: boolean;
   showControls?: boolean;
diff --git a/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.jsx b/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.jsx
index f197db4a5b9..cae0d890549 100644
--- a/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.jsx
+++ b/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.jsx
@@ -2,7 +2,8 @@
 import cx from "classnames";
 import PropTypes from "prop-types";
 import { createRef, forwardRef, Component } from "react";
-import ReactDOM from "react-dom";
+import { findDOMNode } from "react-dom";
+import { createRoot } from "react-dom/client";
 import { connect } from "react-redux";
 import { Grid, ScrollSync } from "react-virtualized";
 import { t } from "ttag";
@@ -158,6 +159,7 @@ class TableInteractive extends Component {
     this._div.style.position = "absolute";
     this._div.style.visibility = "hidden";
     this._div.style.zIndex = "-1";
+    this._root = undefined;
 
     if (this.props.isEmbeddingSdk) {
       const rootElement = document.getElementById(
@@ -317,9 +319,11 @@ class TableInteractive extends Component {
       data: { cols, rows },
     } = this.props;
 
-    ReactDOM.render(
+    this._root = createRoot(this._div);
+
+    this._root.render(
       <EmotionCacheProvider>
-        <div style={{ display: "flex" }}>
+        <div style={{ display: "flex" }} ref={this.onMeasureHeaderRender}>
           {cols.map((column, columnIndex) => (
             <div className="fake-column" key={"column-" + columnIndex}>
               {this.tableHeaderRenderer({
@@ -341,42 +345,47 @@ class TableInteractive extends Component {
           ))}
         </div>
       </EmotionCacheProvider>,
-      this._div,
-      () => {
-        const contentWidths = [].map.call(
-          this._div.getElementsByClassName("fake-column"),
-          columnElement => columnElement.offsetWidth,
-        );
-
-        const columnWidths = cols.map((col, index) => {
-          if (this.columnNeedsResize) {
-            if (
-              this.columnNeedsResize[index] &&
-              !this.columnHasResized[index]
-            ) {
-              this.columnHasResized[index] = true;
-              return contentWidths[index] + 1; // + 1 to make sure it doen't wrap?
-            } else if (this.state.columnWidths[index]) {
-              return this.state.columnWidths[index];
-            } else {
-              return 0;
-            }
-          } else {
-            return contentWidths[index] + 1;
-          }
-        });
+    );
+  }
 
-        // Doing this on next tick makes sure it actually gets removed on initial measure
-        setTimeout(() => {
-          ReactDOM.unmountComponentAtNode(this._div);
-        }, 0);
+  onMeasureHeaderRender = div => {
+    const {
+      data: { cols },
+    } = this.props;
 
-        delete this.columnNeedsResize;
+    if (div === null) {
+      return;
+    }
 
-        this.setState({ contentWidths, columnWidths }, this.recomputeGridSize);
-      },
+    const contentWidths = [].map.call(
+      div.getElementsByClassName("fake-column"),
+      columnElement => columnElement.offsetWidth,
     );
-  }
+
+    const columnWidths = cols.map((col, index) => {
+      if (this.columnNeedsResize) {
+        if (this.columnNeedsResize[index] && !this.columnHasResized[index]) {
+          this.columnHasResized[index] = true;
+          return contentWidths[index] + 1; // + 1 to make sure it doen't wrap?
+        } else if (this.state.columnWidths[index]) {
+          return this.state.columnWidths[index];
+        } else {
+          return 0;
+        }
+      } else {
+        return contentWidths[index] + 1;
+      }
+    });
+
+    // Doing this on next tick makes sure it actually gets removed on initial measure
+    setTimeout(() => {
+      this._root.unmount();
+    }, 0);
+
+    delete this.columnNeedsResize;
+
+    this.setState({ contentWidths, columnWidths }, this.recomputeGridSize);
+  };
 
   recomputeGridSize = () => {
     if (this.header && this.grid) {
@@ -1011,7 +1020,7 @@ class TableInteractive extends Component {
       return;
     }
 
-    const scrollOffset = ReactDOM.findDOMNode(this.grid)?.scrollTop || 0;
+    const scrollOffset = findDOMNode(this.grid)?.scrollTop || 0;
 
     // infer row index from mouse position when we hover the gutter column
     if (event?.currentTarget?.id === "gutter-column") {
@@ -1278,7 +1287,7 @@ class TableInteractive extends Component {
   }
 
   _benchmark() {
-    const grid = ReactDOM.findDOMNode(this.grid);
+    const grid = findDOMNode(this.grid);
     const height = grid.scrollHeight;
     let top = 0;
     let start = Date.now();
diff --git a/frontend/src/metabase/visualizations/components/TableSimple/TableSimple.tsx b/frontend/src/metabase/visualizations/components/TableSimple/TableSimple.tsx
index 8b81c7d5858..06c2d623a5f 100644
--- a/frontend/src/metabase/visualizations/components/TableSimple/TableSimple.tsx
+++ b/frontend/src/metabase/visualizations/components/TableSimple/TableSimple.tsx
@@ -164,7 +164,7 @@ function TableSimpleInner({
   );
 
   const renderColumnHeader = useCallback(
-    (col, colIndex: number) => {
+    (col: DatasetColumn, colIndex: number) => {
       const iconName = sortDirection === "desc" ? "chevrondown" : "chevronup";
       const onClick = () => setSort(colIndex);
       return (
diff --git a/frontend/src/metabase/visualizations/components/Visualization/Visualization-themed.unit.spec.tsx b/frontend/src/metabase/visualizations/components/Visualization/Visualization-themed.unit.spec.tsx
index a46559efdd4..f940c4420c7 100644
--- a/frontend/src/metabase/visualizations/components/Visualization/Visualization-themed.unit.spec.tsx
+++ b/frontend/src/metabase/visualizations/components/Visualization/Visualization-themed.unit.spec.tsx
@@ -1,6 +1,6 @@
 import { renderWithProviders, screen } from "__support__/ui";
+import { delay } from "__support__/utils";
 import { NumberColumn, StringColumn } from "__support__/visualizations";
-import { delay } from "metabase/lib/promise";
 import registerVisualizations from "metabase/visualizations/register";
 import { createMockCard } from "metabase-types/api/mocks";
 
diff --git a/frontend/src/metabase/visualizations/components/Visualization/Visualization.unit.spec.js b/frontend/src/metabase/visualizations/components/Visualization/Visualization.unit.spec.js
index aea355ec2fc..23e1fe1aaae 100644
--- a/frontend/src/metabase/visualizations/components/Visualization/Visualization.unit.spec.js
+++ b/frontend/src/metabase/visualizations/components/Visualization/Visualization.unit.spec.js
@@ -1,7 +1,7 @@
 import { renderWithProviders, screen } from "__support__/ui";
+import { delay } from "__support__/utils";
 import { NumberColumn, StringColumn } from "__support__/visualizations";
 import { color } from "metabase/lib/colors";
-import { delay } from "metabase/lib/promise";
 import Visualization from "metabase/visualizations/components/Visualization";
 import registerVisualizations from "metabase/visualizations/register";
 import { createMockCard } from "metabase-types/api/mocks";
diff --git a/frontend/src/metabase/visualizations/components/settings/ChartSettingsListColumns/ChartSettingsListColumns.tsx b/frontend/src/metabase/visualizations/components/settings/ChartSettingsListColumns/ChartSettingsListColumns.tsx
index 71575d035ee..15fc8bba604 100644
--- a/frontend/src/metabase/visualizations/components/settings/ChartSettingsListColumns/ChartSettingsListColumns.tsx
+++ b/frontend/src/metabase/visualizations/components/settings/ChartSettingsListColumns/ChartSettingsListColumns.tsx
@@ -69,7 +69,7 @@ function ChartSettingsListColumns({
   );
 
   const onColumnSettingsClick = useCallback(
-    (fieldIdOrFieldRef, targetElement: Element) => {
+    (fieldIdOrFieldRef: FieldIdOrFieldRef, targetElement: Element) => {
       const column = columns.find(
         column =>
           column.id === fieldIdOrFieldRef ||
diff --git a/frontend/src/metabase/visualizations/visualizations/CartesianChart/CartesianChart.styled.tsx b/frontend/src/metabase/visualizations/visualizations/CartesianChart/CartesianChart.styled.tsx
index 01731362a7e..f61bf5c58c6 100644
--- a/frontend/src/metabase/visualizations/visualizations/CartesianChart/CartesianChart.styled.tsx
+++ b/frontend/src/metabase/visualizations/visualizations/CartesianChart/CartesianChart.styled.tsx
@@ -35,7 +35,6 @@ export const CartesianChartLegendLayout = styled(LegendLayout)`
   flex: 1 1 auto;
 `;
 
-// @ts-expect-error emotion does not accept the `WrappedComponent` class type
 // created in ExplicitSize
 export const CartesianChartRenderer = styled(ResponsiveEChartsRenderer)`
   height: 100%;
diff --git a/frontend/src/metabase/visualizations/visualizations/CartesianChart/CartesianChart.tsx b/frontend/src/metabase/visualizations/visualizations/CartesianChart/CartesianChart.tsx
index 4e142af172a..a9a18e20791 100644
--- a/frontend/src/metabase/visualizations/visualizations/CartesianChart/CartesianChart.tsx
+++ b/frontend/src/metabase/visualizations/visualizations/CartesianChart/CartesianChart.tsx
@@ -118,8 +118,8 @@ function _CartesianChart(props: VisualizationProps) {
         onRemoveSeries={onRemoveSeries}
         onHoverChange={onHoverChange}
       >
+        {/**@ts-expect-error emotion does not properly provide prop types due */}
         <CartesianChartRenderer
-          // @ts-expect-error emotion does not properly provide prop types due
           // to it not working with the `WrappedComponent` class defined in
           // ExplicitSize
           option={option}
diff --git a/frontend/src/types/ban-evil-use-callback.d.ts b/frontend/src/types/ban-evil-use-callback.d.ts
deleted file mode 100644
index 94b0aa19371..00000000000
--- a/frontend/src/types/ban-evil-use-callback.d.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-declare namespace React {
-  // useCallback parameters are implicitly typed to any.
-  // This override has the effect of forcing you to write types any parameters you want to use.
-  // See https://github.com/DefinitelyTyped/DefinitelyTyped/issues/52873
-  function useCallback<T extends (...args: unknown[]) => unknown>(
-    callback: T,
-    deps: readonly unknown[],
-  ): T;
-}
diff --git a/frontend/test/__support__/utils.ts b/frontend/test/__support__/utils.ts
index 6ce60de4d95..aee8a258683 100644
--- a/frontend/test/__support__/utils.ts
+++ b/frontend/test/__support__/utils.ts
@@ -1,4 +1,12 @@
+import { act } from "./ui";
+
 export const getNextId = (() => {
   let id = 0;
   return () => ++id;
 })();
+
+export async function delay(duration: number) {
+  await act(async () => {
+    await new Promise(resolve => setTimeout(resolve, duration));
+  });
+}
diff --git a/package.json b/package.json
index a0b5579ec90..106cac4c024 100644
--- a/package.json
+++ b/package.json
@@ -13,10 +13,10 @@
     "@dnd-kit/core": "^6.0.8",
     "@dnd-kit/modifiers": "^6.0.1",
     "@dnd-kit/sortable": "^8.0.0",
-    "@emotion/cache": "^11.10.5",
+    "@emotion/cache": "^11.11.0",
     "@emotion/is-prop-valid": "^1.1.1",
-    "@emotion/react": "^11.10.5",
-    "@emotion/styled": "^11.10.5",
+    "@emotion/react": "^11.11.0",
+    "@emotion/styled": "^11.11.0",
     "@mantine/core": "^6.0.13",
     "@mantine/dates": "^6.0.13",
     "@mantine/hooks": "^6.0.13",
@@ -25,6 +25,7 @@
     "@snowplow/browser-tracker": "^3.1.6",
     "@tanstack/react-virtual": "^3.1.2",
     "@tippyjs/react": "^4.2.6",
+    "@types/react-router": "3.0.27",
     "@types/redux-auth-wrapper": "^2.0.16",
     "@visx/axis": "1.8.0",
     "@visx/clip-path": "^2.1.0",
@@ -33,7 +34,7 @@
     "@visx/scale": "1.7.0",
     "@visx/shape": "2.12.2",
     "@visx/text": "1.7.0",
-    "ace-builds": "^1.4.13",
+    "ace-builds": "^1.4.14",
     "classnames": "^2.1.3",
     "color": "^4.2.3",
     "crc-32": "^1.2.2",
@@ -48,6 +49,7 @@
     "dayjs": "^1.10.4",
     "diff": "^3.2.0",
     "echarts": "^5.5.0",
+    "fast-text-encoding": "^1.0.6",
     "formik": "^2.4.5",
     "hast-util-from-html": "^2.0.1",
     "hast-util-to-html": "^9.0.0",
@@ -74,20 +76,20 @@
     "postcss-discard-comments": "^6.0.1",
     "prop-types": "^15.5.7",
     "re-reselect": "^4.0.1",
-    "react": "^17.0.2",
-    "react-ace": "^9.5.0",
+    "react": "^18.2.0",
+    "react-ace": "^10.1.0",
     "react-ansi-style": "^1.0.0",
     "react-beautiful-dnd": "^13.1.1",
     "react-color": "^2.14.1",
     "react-copy-to-clipboard": "^5.0.1",
     "react-dnd": "4",
     "react-dnd-html5-backend": "4",
-    "react-dom": "^17.0.2",
+    "react-dom": "^18.2.0",
     "react-draggable": "^4.4.5",
     "react-dropzone": "^14.2.3",
     "react-grid-layout": "^1.2.5",
     "react-innertext": "^1.1.5",
-    "react-is": "^17.0.2",
+    "react-is": "^18.2.0",
     "react-markdown": "^8.0.4",
     "react-motion": "0.5.2",
     "react-redux": "^8.0.5",
@@ -146,7 +148,7 @@
     "@swc/core": "^1.3.99",
     "@testing-library/cypress": "^9.0.0",
     "@testing-library/jest-dom": "^6.4.2",
-    "@testing-library/react": "^12.1.5",
+    "@testing-library/react": "^15.0.2",
     "@testing-library/react-hooks": "^8.0.0",
     "@testing-library/user-event": "^14.5.2",
     "@types/babel__core": "^7.20.4",
@@ -186,17 +188,16 @@
     "@types/prettier": "1.19.1",
     "@types/prop-types": "^15.7.4",
     "@types/raf": "^3.4.0",
-    "@types/react": "^17.0.77",
+    "@types/react": "^18.3.3",
     "@types/react-beautiful-dnd": "^13.1.3",
     "@types/react-color": "^2.17.6",
     "@types/react-copy-to-clipboard": "^5.0.2",
-    "@types/react-dom": "^17.0.11",
+    "@types/react-dom": "^18.2.17",
     "@types/react-grid-layout": "^1.1.3",
-    "@types/react-is": "^17.0.3",
+    "@types/react-is": "^18.2.4",
     "@types/react-motion": "0.0.32",
     "@types/react-redux": "^7.1.24",
     "@types/react-resizable": "^3.0.4",
-    "@types/react-router": "3.0.28",
     "@types/react-router-redux": "^4.0.53",
     "@types/react-textarea-autosize": "^4.3.6",
     "@types/react-transition-group": "^4.4.5",
@@ -303,8 +304,8 @@
     "jsdom": "^22.1.0",
     "json5": "2.2.2",
     "nth-check": "2.0.1",
-    "@types/react": "^17.0.77",
-    "@types/react-dom": "^17.0.11",
+    "@types/react": "^18.2.42",
+    "@types/react-dom": "^18.2.17",
     "set-value": "4.0.1",
     "source-map-js": "1.2.0",
     "unset-value": "2.0.1",
diff --git a/yarn.lock b/yarn.lock
index 545ca8add15..44955fe812f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1589,7 +1589,7 @@
     source-map "^0.5.7"
     stylis "4.2.0"
 
-"@emotion/cache@^11.10.5", "@emotion/cache@^11.11.0":
+"@emotion/cache@^11.11.0":
   version "11.11.0"
   resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.11.0.tgz#809b33ee6b1cb1a625fef7a45bc568ccd9b8f3ff"
   integrity sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==
@@ -1605,27 +1605,34 @@
   resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43"
   integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==
 
-"@emotion/is-prop-valid@^1.1.1", "@emotion/is-prop-valid@^1.2.1":
+"@emotion/is-prop-valid@^1.1.1":
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz#23116cf1ed18bfeac910ec6436561ecb1a3885cc"
   integrity sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==
   dependencies:
     "@emotion/memoize" "^0.8.1"
 
+"@emotion/is-prop-valid@^1.2.2":
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz#d4175076679c6a26faa92b03bb786f9e52612337"
+  integrity sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==
+  dependencies:
+    "@emotion/memoize" "^0.8.1"
+
 "@emotion/memoize@^0.8.1":
   version "0.8.1"
   resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17"
   integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==
 
-"@emotion/react@^11.10.5":
-  version "11.11.1"
-  resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.11.1.tgz#b2c36afac95b184f73b08da8c214fdf861fa4157"
-  integrity sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==
+"@emotion/react@^11.11.0":
+  version "11.11.4"
+  resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.11.4.tgz#3a829cac25c1f00e126408fab7f891f00ecc3c1d"
+  integrity sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==
   dependencies:
     "@babel/runtime" "^7.18.3"
     "@emotion/babel-plugin" "^11.11.0"
     "@emotion/cache" "^11.11.0"
-    "@emotion/serialize" "^1.1.2"
+    "@emotion/serialize" "^1.1.3"
     "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1"
     "@emotion/utils" "^1.2.1"
     "@emotion/weak-memoize" "^0.3.1"
@@ -1642,20 +1649,31 @@
     "@emotion/utils" "^1.2.1"
     csstype "^3.0.2"
 
+"@emotion/serialize@^1.1.3", "@emotion/serialize@^1.1.4":
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.4.tgz#fc8f6d80c492cfa08801d544a05331d1cc7cd451"
+  integrity sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==
+  dependencies:
+    "@emotion/hash" "^0.9.1"
+    "@emotion/memoize" "^0.8.1"
+    "@emotion/unitless" "^0.8.1"
+    "@emotion/utils" "^1.2.1"
+    csstype "^3.0.2"
+
 "@emotion/sheet@^1.2.2":
   version "1.2.2"
   resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.2.tgz#d58e788ee27267a14342303e1abb3d508b6d0fec"
   integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==
 
-"@emotion/styled@^11.10.5":
-  version "11.11.0"
-  resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.11.0.tgz#26b75e1b5a1b7a629d7c0a8b708fbf5a9cdce346"
-  integrity sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==
+"@emotion/styled@^11.11.0":
+  version "11.11.5"
+  resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.11.5.tgz#0c5c8febef9d86e8a926e663b2e5488705545dfb"
+  integrity sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==
   dependencies:
     "@babel/runtime" "^7.18.3"
     "@emotion/babel-plugin" "^11.11.0"
-    "@emotion/is-prop-valid" "^1.2.1"
-    "@emotion/serialize" "^1.1.2"
+    "@emotion/is-prop-valid" "^1.2.2"
+    "@emotion/serialize" "^1.1.4"
     "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1"
     "@emotion/utils" "^1.2.1"
 
@@ -4389,15 +4407,15 @@
     "@babel/runtime" "^7.14.6"
     "@testing-library/dom" "^8.1.0"
 
-"@testing-library/dom@^8.0.0":
-  version "8.20.1"
-  resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.20.1.tgz#2e52a32e46fc88369eef7eef634ac2a192decd9f"
-  integrity sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==
+"@testing-library/dom@^10.0.0":
+  version "10.0.0"
+  resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-10.0.0.tgz#ae1ab88aad35a728a38264041163174cafd7e8dd"
+  integrity sha512-PmJPnogldqoVFf+EwbHvbBJ98MmqASV8kLrBYgsDNxQcFMeIS7JFL48sfyXvuMtgmWO/wMhh25odr+8VhDmn4g==
   dependencies:
     "@babel/code-frame" "^7.10.4"
     "@babel/runtime" "^7.12.5"
     "@types/aria-query" "^5.0.1"
-    aria-query "5.1.3"
+    aria-query "5.3.0"
     chalk "^4.1.0"
     dom-accessibility-api "^0.5.9"
     lz-string "^1.5.0"
@@ -4453,14 +4471,14 @@
     "@babel/runtime" "^7.12.5"
     react-error-boundary "^3.1.0"
 
-"@testing-library/react@^12.1.5":
-  version "12.1.5"
-  resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b"
-  integrity sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg==
+"@testing-library/react@^15.0.2":
+  version "15.0.2"
+  resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-15.0.2.tgz#d0fd7e9c41b819557639acf5f18e4cd1007ec295"
+  integrity sha512-5mzIpuytB1ctpyywvyaY2TAAUQVCZIGqwiqFQf6u9lvj/SJQepGUzNV18Xpk+NLCaCE2j7CWrZE0tEf9xLZYiQ==
   dependencies:
     "@babel/runtime" "^7.12.5"
-    "@testing-library/dom" "^8.0.0"
-    "@types/react-dom" "<18.0.0"
+    "@testing-library/dom" "^10.0.0"
+    "@types/react-dom" "^18.0.0"
 
 "@testing-library/user-event@^14.5.2":
   version "14.5.2"
@@ -4881,7 +4899,7 @@
   dependencies:
     "@types/unist" "*"
 
-"@types/history@3.2.5", "@types/history@^3.2.5":
+"@types/history@3.2.5", "@types/history@^3", "@types/history@^3.2.5":
   version "3.2.5"
   resolved "https://registry.yarnpkg.com/@types/history/-/history-3.2.5.tgz#be614208f1a10a20c414b4d3762d1f2b65b53ae4"
   integrity sha512-TqWYI0mlqS5qhH8MHgJJs0RcgvvZLxkn0bi2qK07liZqP7M9rl1kpJ6TCAUo5Cp4vP5DObIqHcky0MWyyQojVQ==
@@ -5286,12 +5304,12 @@
   dependencies:
     "@types/react" "*"
 
-"@types/react-dom@<18.0.0", "@types/react-dom@^17.0.11":
-  version "17.0.25"
-  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.25.tgz#e0e5b3571e1069625b3a3da2b279379aa33a0cb5"
-  integrity sha512-urx7A7UxkZQmThYA4So0NelOVjx3V4rNFVJwp0WZlbIK5eM4rNJDiN3R/E9ix0MBh6kAEojk/9YL+Te6D9zHNA==
+"@types/react-dom@^18.0.0", "@types/react-dom@^18.2.17":
+  version "18.2.25"
+  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.25.tgz#2946a30081f53e7c8d585eb138277245caedc521"
+  integrity sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA==
   dependencies:
-    "@types/react" "^17"
+    "@types/react" "*"
 
 "@types/react-grid-layout@^1.1.3":
   version "1.1.3"
@@ -5300,10 +5318,10 @@
   dependencies:
     "@types/react" "*"
 
-"@types/react-is@^17.0.3":
-  version "17.0.3"
-  resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-17.0.3.tgz#2d855ba575f2fc8d17ef9861f084acc4b90a137a"
-  integrity sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==
+"@types/react-is@^18.2.4":
+  version "18.2.4"
+  resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-18.2.4.tgz#95a92829de452662348ce08349ca65623c50daf7"
+  integrity sha512-wBc7HgmbCcrvw0fZjxbgz/xrrlZKzEqmABBMeSvpTvdm25u6KI6xdIi9pRE2G0C1Lw5ETFdcn4UbYZ4/rpqUYw==
   dependencies:
     "@types/react" "*"
 
@@ -5339,12 +5357,12 @@
     "@types/history" "^3.2.5"
     redux "^3.6.0"
 
-"@types/react-router@3.0.28":
-  version "3.0.28"
-  resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-3.0.28.tgz#ae457228e6934f6664e6b092afd7f8e1bb6eee3a"
-  integrity sha512-cf3V8fSBLAzmICC8l5qbnrJhdjF0ia8K0EZwL8IpJDBQN6ieHDdw8t1+9regufQxEVip1T4tCQRwhAmy26Zn2A==
+"@types/react-router@3.0.27":
+  version "3.0.27"
+  resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-3.0.27.tgz#db78ffc4141e5b2d5b0e29bb5565094c05d864d3"
+  integrity sha512-Sq4bfTZ8TuHFTHXyzA3tu/BTGm/d4B8CwtOK4qOyJNoJMkY87qMOazkBizMHNpszVR1+kYOCNLjPdh/wAD7ESw==
   dependencies:
-    "@types/history" "^3.2.5"
+    "@types/history" "^3"
     "@types/react" "*"
 
 "@types/react-textarea-autosize@^4.3.6":
@@ -5369,13 +5387,20 @@
     "@types/prop-types" "*"
     "@types/react" "*"
 
-"@types/react@*", "@types/react@^17", "@types/react@^17.0.77":
-  version "17.0.77"
-  resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.77.tgz#83633bbd5c2e8e07750e17c6c9c1ed7eecf869d3"
-  integrity sha512-iDAZD0Oq82JyO/EfLI12s9EgE+qqshyURjJn4o8E3bF0D69ScuQOF2/+KE++Zt09y8nSw3Bb3JWBBh/g5H87cg==
+"@types/react@*", "@types/react@^18.2.42":
+  version "18.2.77"
+  resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.77.tgz#af2f857b6a6dfb6ca89ec102ebc147b1f1616880"
+  integrity sha512-CUT9KUUF+HytDM7WiXKLF9qUSg4tGImwy4FXTlfEDPEkkNUzJ7rVFolYweJ9fS1ljoIaP7M7Rdjc5eUm/Yu5AA==
+  dependencies:
+    "@types/prop-types" "*"
+    csstype "^3.0.2"
+
+"@types/react@^18.3.3":
+  version "18.3.3"
+  resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.3.tgz#9679020895318b0915d7a3ab004d92d33375c45f"
+  integrity sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==
   dependencies:
     "@types/prop-types" "*"
-    "@types/scheduler" "*"
     csstype "^3.0.2"
 
 "@types/reactcss@*":
@@ -5418,11 +5443,6 @@
   resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.1.tgz#d8f1c0d0dc23afad6dc16a9e993a0865774b4065"
   integrity sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==
 
-"@types/scheduler@*":
-  version "0.16.2"
-  resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
-  integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
-
 "@types/semver@^7.3.12":
   version "7.3.13"
   resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91"
@@ -6431,10 +6451,10 @@ accepts@~1.3.8:
     mime-types "~2.1.34"
     negotiator "0.6.3"
 
-ace-builds@^1.4.13:
-  version "1.22.1"
-  resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.22.1.tgz#3d765d1cfffc79226e372b6353750fd997816154"
-  integrity sha512-o5RGTPBIiRxguWNors3pT6KuLqj0a2NvNLoqir7/2LLiFm34PJV3BMq4sl9kjPayo4+lmd99m6zAq+XPdhyHQA==
+ace-builds@^1.4.14:
+  version "1.33.0"
+  resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.33.0.tgz#3b6fb993a4b1d3f2b5923aecded098e13e6527d8"
+  integrity sha512-PDvytkZNvAfuh+PaP5Oy3l3sBGd7xMk4NsB+4w/w1e3gjBqEOGeJwcX+wF/SB6mLtT3VfJLrhDNPT3eaCjtR3w==
 
 acorn-import-assertions@^1.7.6:
   version "1.8.0"
@@ -6754,12 +6774,12 @@ aria-hidden@^1.1.3:
   dependencies:
     tslib "^2.0.0"
 
-aria-query@5.1.3:
-  version "5.1.3"
-  resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e"
-  integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==
+aria-query@5.3.0:
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e"
+  integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==
   dependencies:
-    deep-equal "^2.0.5"
+    dequal "^2.0.3"
 
 aria-query@^5.0.0:
   version "5.0.0"
@@ -6781,7 +6801,7 @@ arr-union@^3.1.0:
   resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
   integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
 
-array-buffer-byte-length@^1.0.0, array-buffer-byte-length@^1.0.1:
+array-buffer-byte-length@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f"
   integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==
@@ -7138,13 +7158,6 @@ available-typed-arrays@^1.0.6:
   resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz#ac812d8ce5a6b976d738e1c45f08d0b00bc7d725"
   integrity sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==
 
-available-typed-arrays@^1.0.7:
-  version "1.0.7"
-  resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846"
-  integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==
-  dependencies:
-    possible-typed-array-names "^1.0.0"
-
 aws-sdk@^2.840.0:
   version "2.1602.0"
   resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1602.0.tgz#d29ec8467b022a1f6f81c60705f06e6593cb75db"
@@ -9555,30 +9568,6 @@ dedent@^0.7.0:
   resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
   integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=
 
-deep-equal@^2.0.5:
-  version "2.2.3"
-  resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1"
-  integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==
-  dependencies:
-    array-buffer-byte-length "^1.0.0"
-    call-bind "^1.0.5"
-    es-get-iterator "^1.1.3"
-    get-intrinsic "^1.2.2"
-    is-arguments "^1.1.1"
-    is-array-buffer "^3.0.2"
-    is-date-object "^1.0.5"
-    is-regex "^1.1.4"
-    is-shared-array-buffer "^1.0.2"
-    isarray "^2.0.5"
-    object-is "^1.1.5"
-    object-keys "^1.1.1"
-    object.assign "^4.1.4"
-    regexp.prototype.flags "^1.5.1"
-    side-channel "^1.0.4"
-    which-boxed-primitive "^1.0.2"
-    which-collection "^1.0.1"
-    which-typed-array "^1.1.13"
-
 deep-extend@^0.6.0:
   version "0.6.0"
   resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
@@ -9756,7 +9745,7 @@ depd@~1.1.2:
   resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
   integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
 
-dequal@^2.0.0, dequal@^2.0.2:
+dequal@^2.0.0, dequal@^2.0.2, dequal@^2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be"
   integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==
@@ -10436,21 +10425,6 @@ es-get-iterator@^1.0.2:
     is-string "^1.0.5"
     isarray "^2.0.5"
 
-es-get-iterator@^1.1.3:
-  version "1.1.3"
-  resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6"
-  integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==
-  dependencies:
-    call-bind "^1.0.2"
-    get-intrinsic "^1.1.3"
-    has-symbols "^1.0.3"
-    is-arguments "^1.1.1"
-    is-map "^2.0.2"
-    is-set "^2.0.2"
-    is-string "^1.0.7"
-    isarray "^2.0.5"
-    stop-iteration-iterator "^1.0.0"
-
 es-module-lexer@^0.9.0:
   version "0.9.3"
   resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19"
@@ -11303,6 +11277,11 @@ fast-shallow-equal@^1.0.0:
   resolved "https://registry.yarnpkg.com/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz#d4dcaf6472440dcefa6f88b98e3251e27f25628b"
   integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==
 
+fast-text-encoding@^1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz#0aa25f7f638222e3396d72bf936afcf1d42d6867"
+  integrity sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==
+
 fastest-levenshtein@^1.0.12:
   version "1.0.12"
   resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2"
@@ -12436,7 +12415,7 @@ has-tostringtag@^1.0.0:
   dependencies:
     has-symbols "^1.0.2"
 
-has-tostringtag@^1.0.1, has-tostringtag@^1.0.2:
+has-tostringtag@^1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc"
   integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
@@ -13294,7 +13273,7 @@ internal-slot@^1.0.3:
     has "^1.0.3"
     side-channel "^1.0.4"
 
-internal-slot@^1.0.4, internal-slot@^1.0.7:
+internal-slot@^1.0.7:
   version "1.0.7"
   resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802"
   integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==
@@ -13400,7 +13379,7 @@ is-arguments@^1.0.4:
   resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3"
   integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==
 
-is-arguments@^1.1.0, is-arguments@^1.1.1:
+is-arguments@^1.1.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
   integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==
@@ -13408,7 +13387,7 @@ is-arguments@^1.1.0, is-arguments@^1.1.1:
     call-bind "^1.0.2"
     has-tostringtag "^1.0.0"
 
-is-array-buffer@^3.0.2, is-array-buffer@^3.0.4:
+is-array-buffer@^3.0.4:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98"
   integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==
@@ -13526,13 +13505,6 @@ is-date-object@^1.0.1:
   resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
   integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
 
-is-date-object@^1.0.5:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f"
-  integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==
-  dependencies:
-    has-tostringtag "^1.0.0"
-
 is-decimal@^1.0.0:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5"
@@ -13667,11 +13639,6 @@ is-map@^2.0.2:
   resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127"
   integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==
 
-is-map@^2.0.3:
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e"
-  integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==
-
 is-nan@^1.2.1:
   version "1.3.2"
   resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d"
@@ -13799,11 +13766,6 @@ is-set@^2.0.2:
   resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec"
   integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==
 
-is-set@^2.0.3:
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d"
-  integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==
-
 is-shared-array-buffer@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79"
@@ -13906,11 +13868,6 @@ is-uuid@^1.0.2:
   resolved "https://registry.yarnpkg.com/is-uuid/-/is-uuid-1.0.2.tgz#ad1898ddf154947c25c8e54966f48604e9caecc4"
   integrity sha512-tCByphFcJgf2qmiMo5hMCgNAquNSagOetVetDvBXswGkNfoyEMvGH1yDlF8cbZbKnbVBr4Y5/rlpMz9umxyBkQ==
 
-is-weakmap@^2.0.2:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd"
-  integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==
-
 is-weakref@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
@@ -13918,14 +13875,6 @@ is-weakref@^1.0.2:
   dependencies:
     call-bind "^1.0.2"
 
-is-weakset@^2.0.3:
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.3.tgz#e801519df8c0c43e12ff2834eead84ec9e624007"
-  integrity sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==
-  dependencies:
-    call-bind "^1.0.7"
-    get-intrinsic "^1.2.4"
-
 is-whitespace-character@^1.0.0:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7"
@@ -17190,14 +17139,6 @@ object-is@^1.0.1:
     define-properties "^1.1.3"
     es-abstract "^1.18.0-next.1"
 
-object-is@^1.1.5:
-  version "1.1.6"
-  resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07"
-  integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==
-  dependencies:
-    call-bind "^1.0.7"
-    define-properties "^1.2.1"
-
 object-keys@^1.0.12, object-keys@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
@@ -17220,7 +17161,7 @@ object.assign@^4.1.2:
     has-symbols "^1.0.1"
     object-keys "^1.1.1"
 
-object.assign@^4.1.4, object.assign@^4.1.5:
+object.assign@^4.1.5:
   version "4.1.5"
   resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0"
   integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==
@@ -18090,11 +18031,6 @@ posix-character-classes@^0.1.0:
   resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
   integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
 
-possible-typed-array-names@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f"
-  integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==
-
 postcss-attribute-case-insensitive@^4.0.1:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz#d93e46b504589e94ac7277b0463226c68041a880"
@@ -19081,12 +19017,12 @@ re-reselect@^4.0.1:
   resolved "https://registry.yarnpkg.com/re-reselect/-/re-reselect-4.0.1.tgz#21a2306d11bbf377ac78687aa46e1a8848974194"
   integrity sha512-xVTNGQy/dAxOolunBLmVMGZ49VUUR1s8jZUiJQb+g1sI63GAv9+a5Jas9yHvdxeUgiZkU9r3gDExDorxHzOgRA==
 
-react-ace@^9.5.0:
-  version "9.5.0"
-  resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-9.5.0.tgz#b6c32b70d404dd821a7e01accc2d76da667ff1f7"
-  integrity sha512-4l5FgwGh6K7A0yWVMQlPIXDItM4Q9zzXRqOae8KkCl6MkOob7sC1CzHxZdOGvV+QioKWbX2p5HcdOVUv6cAdSg==
+react-ace@^10.1.0:
+  version "10.1.0"
+  resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-10.1.0.tgz#d348eac2b16475231779070b6cd16768deed565f"
+  integrity sha512-VkvUjZNhdYTuKOKQpMIZi7uzZZVgzCjM7cLYu6F64V0mejY8a2XTyPUIMszC6A4trbeMIHbK5fYFcT/wkP/8VA==
   dependencies:
-    ace-builds "^1.4.13"
+    ace-builds "^1.4.14"
     diff-match-patch "^1.0.5"
     lodash.get "^4.4.2"
     lodash.isequal "^4.5.0"
@@ -19188,14 +19124,13 @@ react-docgen@^5.0.0:
     node-dir "^0.1.10"
     strip-indent "^3.0.0"
 
-react-dom@^17.0.2:
-  version "17.0.2"
-  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
-  integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
+react-dom@^18.2.0:
+  version "18.2.0"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
+  integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
   dependencies:
     loose-envify "^1.1.0"
-    object-assign "^4.1.1"
-    scheduler "^0.20.2"
+    scheduler "^0.23.0"
 
 react-draggable@^4.0.0, react-draggable@^4.0.3:
   version "4.4.3"
@@ -19283,7 +19218,7 @@ react-is@^17.0.1:
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
   integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==
 
-react-is@^18.0.0:
+react-is@^18.0.0, react-is@^18.2.0:
   version "18.2.0"
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
   integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
@@ -19509,6 +19444,13 @@ react@^17.0.2:
     loose-envify "^1.1.0"
     object-assign "^4.1.1"
 
+react@^18.2.0:
+  version "18.2.0"
+  resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
+  integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
+  dependencies:
+    loose-envify "^1.1.0"
+
 reactcss@^1.2.0:
   version "1.2.3"
   resolved "https://registry.yarnpkg.com/reactcss/-/reactcss-1.2.3.tgz#c00013875e557b1cf0dfd9a368a1c3dab3b548dd"
@@ -19817,7 +19759,7 @@ regexp.prototype.flags@^1.4.3:
     define-properties "^1.2.0"
     functions-have-names "^1.2.3"
 
-regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.2:
+regexp.prototype.flags@^1.5.2:
   version "1.5.2"
   resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334"
   integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==
@@ -20376,6 +20318,13 @@ scheduler@^0.20.2:
     loose-envify "^1.1.0"
     object-assign "^4.1.1"
 
+scheduler@^0.23.0:
+  version "0.23.0"
+  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
+  integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
+  dependencies:
+    loose-envify "^1.1.0"
+
 schema-utils@2.7.0:
   version "2.7.0"
   resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7"
@@ -21258,13 +21207,6 @@ statuses@2.0.1:
   resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
   integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
 
-stop-iteration-iterator@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4"
-  integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==
-  dependencies:
-    internal-slot "^1.0.4"
-
 store2@^2.12.0:
   version "2.12.0"
   resolved "https://registry.yarnpkg.com/store2/-/store2-2.12.0.tgz#e1f1b7e1a59b6083b2596a8d067f6ee88fd4d3cf"
@@ -23703,32 +23645,11 @@ which-boxed-primitive@^1.0.2:
     is-string "^1.0.5"
     is-symbol "^1.0.3"
 
-which-collection@^1.0.1:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0"
-  integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==
-  dependencies:
-    is-map "^2.0.3"
-    is-set "^2.0.3"
-    is-weakmap "^2.0.2"
-    is-weakset "^2.0.3"
-
 which-module@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
   integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
 
-which-typed-array@^1.1.13:
-  version "1.1.15"
-  resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d"
-  integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==
-  dependencies:
-    available-typed-arrays "^1.0.7"
-    call-bind "^1.0.7"
-    for-each "^0.3.3"
-    gopd "^1.0.1"
-    has-tostringtag "^1.0.2"
-
 which-typed-array@^1.1.14:
   version "1.1.14"
   resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.14.tgz#1f78a111aee1e131ca66164d8bdc3ab062c95a06"
-- 
GitLab