Bug description

When we embed a long dashboard with lots of chart, and when we go to the bottom-most chart, eg: table chart, and we click drill by, and choose a dimension, the drill by modal is shown at the top of the iframe, which is not out of sight. When we move the mouse to view the modal contents, and the source chart is out of view, then the drill by modal disappear by itself. The Chrome Inspector console doesn't have anything useful.

When we click the view query, it's not an issue, even though the modal position is at the top, it doesn't auto close / disappear, when the source chart is out of view.

How to reproduce the bug

  1. Create a dashboard with lots of chart
  2. Put a table chart at the bottom of the dashboard
  3. Embed the dashboard inside an iframe
  4. Open the page containing the iframe
  5. Wait for the page and the charts to load
  6. Scroll to the bottom of the page
  7. Click Drill By, and choose a dimension
  8. The Drill By Modal is far at the top of the page
  9. Scroll up to view the Drill By Modal
  10. Ensure that the source chart is out of sight
  11. Voila, the Drill By Modal disappear.

Screenshots/recordings

No response

Superset version

4.0.2

Python version

3.10

Node version

18 or greater

Browser

Chrome

Additional context

No response

Checklist

  • [X] I have searched Superset docs and Slack and didn't find a solution to my problem.
  • [X] I have searched the GitHub issue tracker and didn't find a similar bug report.
  • [X] I have checked Superset's logs for errors and if I found a relevant Python stacktrace, I included it here as text in the "additional context" section.

Comment From: changwuf31

To add more information, this works fine on superset without embedding it in an iframe

Comment From: dosubot[bot]

Hey @changwuf31! I'm here to help you with any bugs, questions, or contributions you might have. Let's tackle this together!

To prevent the "Drill By" modal from disappearing when the source chart is out of view in an embedded Superset dashboard, you need to ensure that the maskClosable property of the Modal component is set to false. This ensures that the modal will not close when clicking outside of it. Here is the relevant code snippet:

<Modal
  show={showModal}
  onHide={onHideModal ?? (() => null)}
  css={css`
    .ant-modal-body {
      display: flex;
      flex-direction: column;
    }
  `}
  title={t('Drill to detail: %s', chartName)}
  footer={
    <ModalFooter exploreChart={exploreChart} canExplore={canExplore} />
  }
  responsive
  resizable
  resizableConfig={{
    minHeight: theme.gridUnit * 128,
    minWidth: theme.gridUnit * 128,
    defaultSize: {
      width: 'auto',
      height: '75vh',
    },
  }}
  draggable
  destroyOnClose
  maskClosable={false}
>
  <DrillDetailPane formData={formData} initialFilters={initialFilters} />
</Modal>

Additionally, ensure that the modal remains visible and functional by handling its visibility and interactions properly. For example, in the Cypress test for the "Drill By" modal, the modal's visibility and interactions are verified to ensure it remains functional even when the source chart is out of view:

cy.get('@drillByModal')
  .find('.draggable-trigger')
  .should('contain', 'Drill by: Table');

cy.get('@drillByModal')
  .find('[data-test="metadata-bar"]')
  .should('be.visible');

cy.get('@drillByModal')
  .find('.ant-breadcrumb')
  .should('be.visible')
  .and('contain', 'gender (boy)')
  .and('contain', '/')
  .and('contain', 'state');

cy.get('@drillByModal')
  .find('[data-test="drill-by-chart"]')
  .should('be.visible')
  .and('contain', 'state')
  .and('contain', 'sum__num');

This configuration and testing approach should prevent the "Drill By" modal from disappearing when the source chart is out of view [1][2][3].

To continue talking to Dosu, mention @dosu.

Comment From: changwuf31

@dosu maskClosable is already {false} and your cypress test doesn't involve any scrolling to make the chart out of sight, it also do not check the chart in embedded dashboard.

Comment From: dosubot[bot]

To prevent the "Drill By" modal from disappearing when the source chart is out of view in an embedded Superset dashboard, you can ensure that the modal remains visible by modifying the Cypress test to handle scrolling and visibility checks. Since maskClosable is already set to false, the modal should not close when clicking outside of it. However, you need to ensure that the modal remains in view even if the source chart is scrolled out of view.

Here is an example of how you can modify the Cypress test to handle this scenario:

import { waitForChartLoad } from 'cypress/utils';
import { SUPPORTED_CHARTS_DASHBOARD } from 'cypress/utils/urls';
import {
  openTopLevelTab,
  SUPPORTED_TIER1_CHARTS,
} from './utils';

const ensureModalInView = () => {
  cy.get('@drillByModal').then($modal => {
    if ($modal.is(':hidden')) {
      cy.get('@drillByModal').scrollIntoView();
    }
  });
};

describe('Drill by modal', () => {
  beforeEach(() => {
    closeModal();
  });
  before(() => {
    cy.visit(SUPPORTED_CHARTS_DASHBOARD);
  });

  describe('Modal actions + Table', () => {
    before(() => {
      closeModal();
      openTopLevelTab('Tier 1');
      SUPPORTED_TIER1_CHARTS.forEach(waitForChartLoad);
    });

    it('opens the modal from the context menu', () => {
      openTableContextMenu('boy');
      drillBy('state').then(intercepted => {
        verifyExpectedFormData(intercepted, {
          groupby: ['state'],
          adhoc_filters: [
            {
              clause: 'WHERE',
              comparator: 'boy',
              expressionType: 'SIMPLE',
              operator: '==',
              operatorId: 'EQUALS',
              subject: 'gender',
            },
          ],
        });
      });

      cy.getBySel('"Drill by: Table-modal"').as('drillByModal');

      ensureModalInView();

      cy.get('@drillByModal')
        .find('.draggable-trigger')
        .should('contain', 'Drill by: Table');

      cy.get('@drillByModal')
        .find('[data-test="metadata-bar"]')
        .should('be.visible');

      cy.get('@drillByModal')
        .find('.ant-breadcrumb')
        .should('be.visible')
        .and('contain', 'gender (boy)')
        .and('contain', '/')
        .and('contain', 'state');

      cy.get('@drillByModal')
        .find('[data-test="drill-by-chart"]')
        .should('be.visible')
        .and('contain', 'state')
        .and('contain', 'sum__num');

      // further drilling
      openTableContextMenu('CA', '[data-test="drill-by-chart"]');
      drillBy('name').then(intercepted => {
        verifyExpectedFormData(intercepted, {
          groupby: ['name'],
          adhoc_filters: [
            {
              clause: 'WHERE',
              comparator: 'boy',
              expressionType: 'SIMPLE',
              operator: '==',
              operatorId: 'EQUALS',
              subject: 'gender',
            },
            {
              clause: 'WHERE',
              comparator: 'CA',
              expressionType: 'SIMPLE',
              operator: '==',
              operatorId: 'EQUALS',
              subject: 'state',
            },
          ],
        });
      });

      ensureModalInView();

      cy.get('@drillByModal')
        .find('[data-test="drill-by-chart"]')
        .should('be.visible')
        .and('not.contain', 'state')
        .and('contain', 'name')
        .and('contain', 'sum__num');

      // undo - back to drill by state
      interceptV1ChartData('drillByUndo');
      interceptFormDataKey();
      cy.get('@drillByModal')
        .find('.ant-breadcrumb')
        .should('be.visible')
        .and('contain', 'gender (boy)')
        .and('contain', '/')
        .and('contain', 'state (CA)')
        .and('contain', 'name')
        .contains('state (CA)')
        .click();
      cy.wait('@drillByUndo').then(intercepted => {
        verifyExpectedFormData(intercepted, {
          groupby: ['state'],
          adhoc_filters: [
            {
              clause: 'WHERE',
              comparator: 'boy',
              expressionType: 'SIMPLE',
              operator: '==',
              operatorId: 'EQUALS',
              subject: 'gender',
            },
          ],
        });
      });

      ensureModalInView();

      cy.get('@drillByModal')
        .find('[data-test="drill-by-chart"]')
        .should('be.visible')
        .and('not.contain', 'name')
        .and('contain', 'state')
        .and('contain', 'sum__num');

      cy.get('@drillByModal')
        .find('.ant-breadcrumb')
        .should('be.visible')
        .and('contain', 'gender (boy)')
        .and('contain', '/')
        .and('not.contain', 'state (CA)')
        .and('not.contain', 'name')
        .and('contain', 'state');

      cy.get('@drillByModal')
        .find('[data-test="drill-by-display-toggle"]')
        .contains('Table')
        .click();

      cy.getBySel('drill-by-chart').should('not.exist');

      cy.get('@drillByModal')
        .find('[data-test="drill-by-results-table"]')
        .should('be.visible');
      cy.wait('@formDataKey').then(intercept => {
        cy.get('@drillByModal')
          .contains('Edit chart')
          .should('have.attr', 'href')
          .and(
            'contain',
            `/explore/?form_data_key=${intercept.response?.body?.key}`,
          );
      });
    });
  });
});

This code ensures that the modal is scrolled into view if it is hidden, thus preventing it from disappearing when the source chart is out of view [1][2].

To continue talking to Dosu, mention @dosu.

Comment From: rusackas

Is anyone still facing this? I suspect it may have been resolved by this: https://github.com/apache/superset/pull/26798

Comment From: rusackas

Ok... assuming this is resolved, then, and closing. A bunch of components and such based on AntD have been replaced since this was last active, so I'm assuming the situation is different, if not resolved. But if I'm mistaken (it sure happens!) then I'm more than happy to reopen.