From 76b0a3f513173477fe2e21599d28f1296288e50b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Pretto?= <info@npretto.com> Date: Thu, 29 Aug 2024 15:14:41 +0200 Subject: [PATCH] locale query string support on public links and static embeds (#47186) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * keep locale in url query params * e2e tests * ugly fix for the missing baseUrl error * applies suggestion from Kelvin to make code more demure * Update e2e/test/scenarios/sharing/public-question.cy.spec.js Co-authored-by: Anton Kulyk <kuliks.anton@gmail.com> * remove it.skip added by mistake * sort imports * handle native question/ SyncedParametersList too * shorter and more accurate comment 😅 * Update e2e/support/helpers/e2e-embedding-helpers.js Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev> --------- Co-authored-by: Anton Kulyk <kuliks.anton@gmail.com> Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev> --- e2e/support/helpers/e2e-embedding-helpers.js | 10 +++-- .../embedding/embedding-dashboard.cy.spec.js | 28 ++++++++++++++ .../embedding/embedding-questions.cy.spec.js | 7 +++- .../sharing/public-dashboard.cy.spec.js | 12 ++++++ .../sharing/public-question.cy.spec.js | 38 +++++++++++++++++++ .../hooks/use-dashboard-url-query.ts | 2 +- .../components/SyncedParametersList.tsx | 2 +- 7 files changed, 92 insertions(+), 7 deletions(-) diff --git a/e2e/support/helpers/e2e-embedding-helpers.js b/e2e/support/helpers/e2e-embedding-helpers.js index cb3ad1254bd..bcfb8533411 100644 --- a/e2e/support/helpers/e2e-embedding-helpers.js +++ b/e2e/support/helpers/e2e-embedding-helpers.js @@ -29,7 +29,11 @@ import { openSharingMenu } from "./e2e-sharing-helpers"; * Programmatically generate token and visit the embedded page for a question or a dashboard * * @param {EmbedPayload} payload - The {@link EmbedPayload} we pass to this function - * @param {{[setFilters]: object, pageStyle: PageStyle, [hideFilters]: string[]}} options + * @param {*} options + * @param {object} [options.setFilters] + * @param {PageStyle} options.pageStyle + * @param {string[]} [options.hideFilters] + * @param {object} [options.qs] * * @example * visitEmbeddedPage(payload, { @@ -40,7 +44,7 @@ import { openSharingMenu } from "./e2e-sharing-helpers"; */ export function visitEmbeddedPage( payload, - { setFilters = {}, hideFilters = [], pageStyle = {}, onBeforeLoad } = {}, + { setFilters = {}, hideFilters = [], pageStyle = {}, onBeforeLoad, qs } = {}, ) { const jwtSignLocation = "e2e/support/external/e2e-jwt-sign.js"; @@ -63,7 +67,7 @@ export function visitEmbeddedPage( cy.visit({ url: urlRoot, - qs: setFilters, + qs: { ...setFilters, ...qs }, onBeforeLoad: window => { onBeforeLoad?.(window); if (urlHash) { diff --git a/e2e/test/scenarios/embedding/embedding-dashboard.cy.spec.js b/e2e/test/scenarios/embedding/embedding-dashboard.cy.spec.js index 55fea4188f4..8a94c8d310c 100644 --- a/e2e/test/scenarios/embedding/embedding-dashboard.cy.spec.js +++ b/e2e/test/scenarios/embedding/embedding-dashboard.cy.spec.js @@ -1,4 +1,5 @@ import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database"; +import { ORDERS_DASHBOARD_ID } from "e2e/support/cypress_sample_instance_data"; import { addOrUpdateDashboardCard, assertEmbeddingParameter, @@ -16,6 +17,7 @@ import { getIframeUrl, getRequiredToggle, goToTab, + main, modal, multiAutocompleteInput, openStaticEmbeddingModal, @@ -646,7 +648,12 @@ describe("scenarios > embedding > dashboard parameters with defaults", () => { }); describeEE("scenarios > embedding > dashboard appearance", () => { + const originalBaseUrl = Cypress.config("baseUrl"); beforeEach(() => { + // Reset the baseUrl to the default value + // needed because we do `Cypress.config("baseUrl", null);` in the iframe test + Cypress.config("baseUrl", originalBaseUrl); + restore(); cy.signInAsAdmin(); setTokenFeatures("all"); @@ -939,6 +946,27 @@ describeEE("scenarios > embedding > dashboard appearance", () => { expect(iframe.clientHeight).to.be.greaterThan(1000); }); }); + + it("should allow to set locale from the `locale` query parameter", () => { + cy.request("PUT", `/api/dashboard/${ORDERS_DASHBOARD_ID}`, { + enable_embedding: true, + }); + cy.signOut(); + + visitEmbeddedPage( + { + resource: { dashboard: ORDERS_DASHBOARD_ID }, + params: {}, + }, + { qs: { locale: "de" } }, + ); + + main().findByText("Februar 11, 2025, 9:40 PM"); + // eslint-disable-next-line no-unscoped-text-selectors -- we don't care where the text is + cy.findByText("exportieren", { exact: false }); + + cy.url().should("include", "locale=de"); + }); }); function openFilterOptions(name) { diff --git a/e2e/test/scenarios/embedding/embedding-questions.cy.spec.js b/e2e/test/scenarios/embedding/embedding-questions.cy.spec.js index bb321cda3fc..8570be2da34 100644 --- a/e2e/test/scenarios/embedding/embedding-questions.cy.spec.js +++ b/e2e/test/scenarios/embedding/embedding-questions.cy.spec.js @@ -6,6 +6,7 @@ import { describeEE, echartsContainer, filterWidget, + main, openStaticEmbeddingModal, popover, restore, @@ -218,8 +219,10 @@ describe("scenarios > embedding > questions", () => { }); }); - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("Februar 11, 2025, 9:40 PM"); + main().findByText("Februar 11, 2025, 9:40 PM"); + main().findByText("Zeilen", { exact: false }); + + cy.url().should("include", "locale=de"); }); }); diff --git a/e2e/test/scenarios/sharing/public-dashboard.cy.spec.js b/e2e/test/scenarios/sharing/public-dashboard.cy.spec.js index a3d17693926..bfcc23b37d2 100644 --- a/e2e/test/scenarios/sharing/public-dashboard.cy.spec.js +++ b/e2e/test/scenarios/sharing/public-dashboard.cy.spec.js @@ -263,6 +263,18 @@ describe("scenarios > public > dashboard", () => { filterWidget().findByText("002").should("be.visible"); }); + + it("should allow to set locale from the `locale` query parameter", () => { + cy.get("@dashboardId").then(id => { + visitPublicDashboard(id, { + params: { locale: "de" }, + }); + }); + + // eslint-disable-next-line no-unscoped-text-selectors -- we don't care where the text is + cy.findByText("Registerkarte als PDF exportieren").should("be.visible"); + cy.url().should("include", "locale=de"); + }); }); describeEE("scenarios [EE] > public > dashboard", () => { diff --git a/e2e/test/scenarios/sharing/public-question.cy.spec.js b/e2e/test/scenarios/sharing/public-question.cy.spec.js index e884f9cae44..4e17f39a359 100644 --- a/e2e/test/scenarios/sharing/public-question.cy.spec.js +++ b/e2e/test/scenarios/sharing/public-question.cy.spec.js @@ -1,9 +1,11 @@ import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database"; import { assertSheetRowsCount, + createNativeQuestion, createPublicQuestionLink, downloadAndAssert, filterWidget, + main, modal, openNativeEditor, openNewPublicLinkDropdown, @@ -194,6 +196,42 @@ describe("scenarios > public > question", () => { }); }); }); + + it("should allow to set locale from the `locale` query parameter", () => { + createNativeQuestion( + { + name: "Native question with a parameter", + native: { + query: + "select '2025-2-11'::DATE as date, {{some_parameter}} as some_parameter ", + "template-tags": { + some_parameter: { + type: "text", + name: "some_parameter", + id: "1e0806a0-155b-4e24-80bc-c050720201d0", + "display-name": "Some Parameter", + default: "some default value", + }, + }, + }, + }, + { wrapId: true }, + ); + + cy.get("@questionId").then(id => { + cy.request("POST", `/api/card/${id}/public_link`).then( + ({ body: { uuid } }) => { + cy.visit( + `/public/question/${uuid}?locale=de&some_parameter=some_value`, + ); + }, + ); + }); + + main().findByText("Februar 11, 2025"); + + cy.url().should("include", "locale=de"); + }); }); const visitPublicURL = () => { diff --git a/frontend/src/metabase/dashboard/hooks/use-dashboard-url-query.ts b/frontend/src/metabase/dashboard/hooks/use-dashboard-url-query.ts index 8f07102d837..232c0246b73 100644 --- a/frontend/src/metabase/dashboard/hooks/use-dashboard-url-query.ts +++ b/frontend/src/metabase/dashboard/hooks/use-dashboard-url-query.ts @@ -122,7 +122,7 @@ export function useDashboardUrlQuery( }, [router, location, selectedTab, dispatch]); } -const QUERY_PARAMS_ALLOW_LIST = ["objectId"]; +const QUERY_PARAMS_ALLOW_LIST = ["objectId", "locale"]; function parseTabId(location: Location) { const slug = location.query?.tab; diff --git a/frontend/src/metabase/query_builder/components/SyncedParametersList.tsx b/frontend/src/metabase/query_builder/components/SyncedParametersList.tsx index da2bdfbd49a..62a53441a7f 100644 --- a/frontend/src/metabase/query_builder/components/SyncedParametersList.tsx +++ b/frontend/src/metabase/query_builder/components/SyncedParametersList.tsx @@ -75,7 +75,7 @@ export const SyncedParametersList = ({ ); }; -const QUERY_PARAMS_ALLOW_LIST = ["objectId"]; +const QUERY_PARAMS_ALLOW_LIST = ["objectId", "locale"]; function buildSearchString(object: Record<string, any>) { const currentSearchParams = querystring.parse( -- GitLab