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