Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keeps preview across different tabs #38271

Merged
merged 16 commits into from Feb 6, 2024

Conversation

WiNloSt
Copy link
Member

@WiNloSt WiNloSt commented Jan 30, 2024

Part of a milestone 6 of
Epic: #35961

Description

Make sure embed preview doesn't reload when changing between tabs/preview toggle/appearance settings

Please note that when going to the overview tab, the preview will be unmounted, so going to the preview again after visiting the overview tab will cause the iframe to reload. This is a compromise for the current codebase. I need to put a lot more work on the overview tab to make that happen, so it didn't happen.

How to verify

Tests both dashboards and questions the same way.

  1. Open static embed modal for a dashboard/question
  2. Go to Parameters tab.
  3. Click preview
  4. Go to Appearance tab. The preview shouldn't rerender.
  5. Modify settings in the Appearance tab, the preview shouldn't rerender.

Demo

https://www.loom.com/share/f700c55d53634f5b974bba30f4f55867

Checklist

  • Tests have been added/updated to cover changes in this PR

@WiNloSt WiNloSt changed the title 35961 keep preview across different tabs Keeps preview across different tabs Jan 31, 2024
@WiNloSt WiNloSt added the no-backport Do not backport this PR to any branch label Jan 31, 2024
@WiNloSt WiNloSt marked this pull request as ready for review February 1, 2024 04:53
Copy link

replay-io bot commented Feb 1, 2024

StatusIn Progress ↗︎ 53 / 54
Commit661f6a3
Results
⚠️ 15 Flaky
2257 Passed

@WiNloSt WiNloSt force-pushed the 35961-keep-preview-across-different-tabs branch from 95de2dc to 22bc55f Compare February 1, 2024 08:29
@@ -224,7 +227,6 @@ export const DashboardControls = ComposedComponent =>
hasNightModeToggle={this.state.theme !== "transparent"}
setRefreshElapsedHook={this.setRefreshElapsedHook}
loadDashboardParams={this.loadDashboardParams}
updateDashboardParams={this.updateDashboardParams}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was unused. You can verify by using a text search of updateDashboardParams, and no file except this file uses this function.

@@ -423,6 +426,94 @@ describe("scenarios > embedding > dashboard parameters with defaults", () => {
});
});

describeEE("scenarios > embedding > dashboard appearance", () => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a new test to ensure:

  • Changing between parameters and appearance tabs doesn't rerender the preview
  • Changing anything under appearance tab doesn't rerender the preview

@@ -16,7 +16,7 @@ import {

const { ORDERS, PRODUCTS } = SAMPLE_DATABASE;

describe("scenarios > embedding > questions ", () => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -142,32 +143,3 @@ describe("issue 37914", () => {
});
});
});

// Code grabbed from https://www.cypress.io/blog/2020/02/12/working-with-iframes-in-cypress
const getIframeDocument = () => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We seem to already have an existing util which does exactly this. So I'm removing a one-off util.

@@ -49,13 +49,11 @@ describeEE("issue 26988", () => {
.click();
popover().findByText("Oswald").click();

cy.wait("@dashboard");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're not rerendering the embed preview when changing appearance settings anymore. There would be only one @dashboard call in this test.

onChangePane={setActivePane}
onChangeDisplayOptions={setDisplayOptions}
/>
<Tabs.Panel value={activeTab}>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit hacky, but essentially, I need the preview to stay in the same component, otherwise, it will unmount and remount when changing between tabs because we have 3 parents (Tabs.Panel) previously. Now it's reduced to just 1.

Copy link
Member

@npretto npretto Feb 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It took me a bit to understand why this is working lol, even with the context of the pr and your comments. Should we add a comment in the code explaining why we're doing this? Otherwise it could be very easy to think "why are they doing this??" when reading the code :D

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, good point, the comment should go into the code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment in 2b8172c

onChangeDisplayOptions={setDisplayOptions}
/>
<Tabs.Panel value={activeTab}>
{activeTab === TABS.Overview ? (
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ternary isn't the best looking thing here, but I need every element to be in the same bracket, otherwise the iframe preview will rerender.

/>
<Tabs.Panel value={activeTab}>
{activeTab === TABS.Overview ? (
<OverviewSettings
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wish I could keep the preview state even when navigating to the Overview tab, but it's a bit hard to extract the content inside it here, so we only keep the embedding state when switching between Parameters and Appearance tabs.

}
previewSlot={
<>
<PreviewModeSelector
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is moved from both ParametersSettings and AppearanceSettings, the structures are similar that's why React doesn't unmount the preview when changing between these 2 tabs.

@@ -29,12 +28,11 @@ function getSignedToken(
});
}

export function getSignedPreviewUrl(
export function getSignedPreviewUrlWithoutHash(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only used in frontend/src/metabase/public/components/EmbedModal/StaticEmbedSetupPane/StaticEmbedSetupPane.tsx so it's safe to modify the return value shape. I removed the hash part because I want the URL part before the hash to stay the same when only the hash is changed.

With the previous implementation, even when the hash is changed, the whole URL will be changed too.

@WiNloSt WiNloSt requested a review from a team February 1, 2024 12:27
Copy link
Member

@npretto npretto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some comments but looks good to me

cy.log("Assert dashboard title");
getIframeBody().findByText(dashboardDetails.name).should("exist");
// We're getting an input element which is 0x0 in size
cy.findByLabelText("Dashboard title").click({ force: true });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of finding the element with that label, can we click the label?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. Do you have an example where we click the labels? I looked up testing-library, and they could only get the input using various matchers e.g. byPlaceholderText, byLabelText, but to find labels I could use findByText which is less specific, or findByRole with a lot of hoops. I'd rather use findByLabelText with force: true because I think it's still more readable.

What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's ok, I tried with https://testing-playground.com/ and it only prompted by findByText

onChangePane={setActivePane}
onChangeDisplayOptions={setDisplayOptions}
/>
<Tabs.Panel value={activeTab}>
Copy link
Member

@npretto npretto Feb 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It took me a bit to understand why this is working lol, even with the context of the pr and your comments. Should we add a comment in the code explaining why we're doing this? Otherwise it could be very easy to think "why are they doing this??" when reading the code :D

@@ -107,19 +113,17 @@ export const StaticEmbedSetupPane = ({
embeddingParams,
});

const iframeUrl = useMemo(
const iframeUrlWithoutHash = useMemo(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this only used in iframeUrl which also uses the hash params?

type: "string/contains",
};

const dashboardDetails = {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@npretto I added this filter, since I found out today that having a parameter will cause the appearance page preview to rerender every time we change any settings

@@ -52,7 +53,9 @@ const dashboardDetails = {

describe("issue 37914", () => {
beforeEach(() => {
cy.intercept("GET", "/api/embed/dashboard/**").as("getEmbed");
cy.intercept("GET", "/api/preview_embed/dashboard/**").as(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The preview didn't seem to work in Cypress before. I've fixed that in frontend/src/metabase/lib/dom.js

@@ -40,7 +42,7 @@ describeEE("issue 26988", () => {
acceptTerms: false,
});

cy.wait("@dashboard");
cy.wait("@previewDashboard");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The preview didn't seem to work in Cypress before. I've fixed that in frontend/src/metabase/lib/dom.js

@@ -26,7 +26,7 @@ window.METABASE = true;
// used for detecting if we're previewing an embed
export const IFRAMED_IN_SELF = (function () {
try {
return window.self !== window.top && window.top.METABASE;
return window.self !== window.parent && window.parent.METABASE;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IFRAMED_IN_SELF was only used in a few places. And all of them relate to how we're rendering the embed preview.

This was failing in Cypress because there are 3 frames in Cypress

  • Cypress
    • Metabase Iframe
      • Embed preview Iframe

But in our application, there are only 2 frames

  • Metabase
    • Embed preview Iframe

For this particular code, when running in Cypress window.parent will get Cypress frame, so the function will return false, which was wrong.

Changing it to window.parent, makes sure it returns the immediate parent frame which will be Metabase frame in both cases.

@WiNloSt WiNloSt requested a review from npretto February 2, 2024 12:35
This was referenced May 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
no-backport Do not backport this PR to any branch .Team/Embedding
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants