Skip to content

feat: allow drill-to-details to a specific chart (optionaly) configured at the dataset level#34785

Open
mistercrunch wants to merge 13 commits into
masterfrom
custom-drillthrough
Open

feat: allow drill-to-details to a specific chart (optionaly) configured at the dataset level#34785
mistercrunch wants to merge 13 commits into
masterfrom
custom-drillthrough

Conversation

@mistercrunch
Copy link
Copy Markdown
Member

@mistercrunch mistercrunch commented Aug 21, 2025

This PR introduces customizable drill-to-details functionality that allows dataset owners to configure which chart should be used when users drill through from any chart using their dataset.

Screenshot 2025-08-20 at 11 41 36 PM Screenshot 2025-08-20 at 11 36 27 PM

Problem: Current drill-to-details uses a one-size-fits-all approach showing all columns in a generic table with no customization options.

Solution: Dataset-level drill-to-details customization allowing association of any existing chart as the drill-to-details target.

Key Benefits:

  • Take advantage of all the features of the Table viz in drill-to-details (column selection, ordering, sorting, aggregation, conditional formatting, link/url support, ...)
  • Flexible chart types: Any chart type or visualization supported
  • Backwards compatible: No-op unless configured, falls back gracefully
  • Cross-dashboard consistency: Same experience across all dashboards

Implementation:

  • Backend: Added drill_through_chart_id column with FK relationship
  • Frontend: ChartSelect component with Dataset Editor integration
  • Enhanced drill modal to render StatefulChart when configured
  • Full TypeScript support with proper type definitions

Walkthrough:

  • create a chart you'd like to see when drilling-to-details for a given dataset
  • edit the dataset properties, settings, pick that chart as your drill-to-details chart
  • open a dashboard with that dataset
  • drill-to-details

@github-actions github-actions Bot added risk:db-migration PRs that require a DB migration api Related to the REST API labels Aug 21, 2025
Copy link
Copy Markdown

@korbit-ai korbit-ai Bot left a comment

Choose a reason for hiding this comment

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

I've completed my review and didn't find any issues.

Files scanned
File Path Reviewed
superset-frontend/src/components/Chart/types.ts
superset/migrations/versions/2025-08-21_01-54_852e99567fe7_.py
superset-frontend/src/features/datasets/types.ts
superset/migrations/versions/2025-08-11_12-43_f56ac3accfc9_drill_through_chart_fk.py
superset-frontend/src/components/Select/ChartSelect.tsx
superset-frontend/src/explore/components/controls/SelectAsyncControl/index.tsx
superset-frontend/src/components/Datasource/DatasourceModal.tsx
superset-frontend/src/components/Chart/DrillDetail/DrillDetailPane.tsx
superset/datasets/schemas.py
superset/datasets/api.py
superset-frontend/src/components/Datasource/DatasourceEditor.jsx
superset/connectors/sqla/models.py

Explore our documentation to understand the languages and file types we support and the files we ignore.

Check out our docs on how you can make Korbit work best for you and your team.

Loving Korbit!? Share us on LinkedIn Reddit and X

@mistercrunch mistercrunch changed the title Custom drillthrough feat: allow drill-to-details to a specific chart (optionaly) configured at the dataset level Aug 21, 2025
@github-actions
Copy link
Copy Markdown
Contributor

@mistercrunch Processing your ephemeral environment request here. Action: up. More information on how to use or configure ephemeral environments

@github-actions
Copy link
Copy Markdown
Contributor

@mistercrunch Ephemeral environment spinning up at http://44.243.96.179:8080. Credentials are 'admin'/'admin'. Please allow several minutes for bootstrapping and startup.

@mistercrunch
Copy link
Copy Markdown
Member Author

While testing, you might hit a weird known issues related to this comment: #23454 (comment)

Refreshing the page fixes it, so you may need to refresh the page between saving the dataset with the new drill-to-chart for it to work in the dashboard.

@mistercrunch
Copy link
Copy Markdown
Member Author

🎪 @unknown Creating ephemeral environment for commit 8148a7c

Action: View workflow
Environment: 8148a7c
Powered by: Superset Showtime

Building and deploying... Watch the labels for progress updates.

@mistercrunch mistercrunch added the 🎪 ⚡ showtime-trigger-start Create new ephemeral environment for this PR label Aug 26, 2025
@github-actions github-actions Bot removed the 🎪 ⚡ showtime-trigger-start Create new ephemeral environment for this PR label Aug 26, 2025
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Sep 3, 2025

🎪 Showtime deployed environment on GHA for f0bb7b6

Environment: http://54.213.17.221:8080 (admin/admin)
Lifetime: 24h auto-cleanup
Updates: New commits create fresh environments automatically

@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented Sep 3, 2025

Bito Automatic Review Failed - Technical Failure

Bito encountered technical difficulties while generating code feedback . To retry, type /review in a comment and save. If the issue persists, contact support@bito.ai and provide the following details:

Agent Run ID: 72dd4a87-cbc1-41a9-9b91-ad08aa31dd78

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Sep 3, 2025

🎪 Showtime is building environment on GHA for e8f275d

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Sep 3, 2025

🎪 Showtime deployed environment on GHA for e8f275d

Environment: http://34.220.106.89:8080 (admin/admin)
Lifetime: 24h auto-cleanup
Updates: New commits create fresh environments automatically

@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented Sep 3, 2025

Bito Automatic Review Failed - Technical Failure

Bito encountered technical difficulties while generating code feedback . To retry, type /review in a comment and save. If the issue persists, contact support@bito.ai and provide the following details:

Agent Run ID: a0c2a37b-3a5b-494b-a480-7cb4a3bd7197

);

// Navigate to the explore page
window.location.href = url;

Check warning

Code scanning / CodeQL

DOM text reinterpreted as HTML Medium

DOM text
is reinterpreted as HTML without escaping meta-characters.

Copilot Autofix

AI 9 months ago

The root cause is the processing of the data-bootstrap DOM attribute, which is parsed as JSON and used to set values like application_root. These values then propagate through helpers, are used to construct URLs, and eventually drive navigation. To fix this, we must ensure that any data loaded from the DOM (specifically, anything parsed from data-bootstrap) is sanitized:

  • For path segments like application_root, ensure they only contain valid path characters and do not contain dangerous meta-characters (e.g., <, >, ", ', ;, and fragments like javascript:, etc).
  • The best and lowest-impact fix is to validate and sanitize application_root (and other similar fields) immediately after parsing data-bootstrap within getBootstrapData(), before any parts of the code base use them for constructing URLs.
  • If application_root is missing or invalid, fall back to the default value.
  • Implement a helper called sanitizeAppRoot, which (i) rejects dangerous characters, (ii) ensures it starts with /, (iii) strips fragments, and (iv) returns a safe string.
  • Use sanitizeAppRoot inside getBootstrapData to guarantee that all uses downstream (e.g., in ensureAppRoot and all calling code) are safe.

This approach avoids altering callers, only changes the population of bootstrap data, requires a single helper implementation, and does not affect functionality.


Suggested changeset 1
superset-frontend/src/utils/getBootstrapData.ts
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/superset-frontend/src/utils/getBootstrapData.ts b/superset-frontend/src/utils/getBootstrapData.ts
--- a/superset-frontend/src/utils/getBootstrapData.ts
+++ b/superset-frontend/src/utils/getBootstrapData.ts
@@ -21,13 +21,49 @@
 
 let cachedBootstrapData: BootstrapData | null = null;
 
+// Helper to sanitize application root paths
+function sanitizeAppRoot(path: string): string {
+  if (typeof path !== 'string') return DEFAULT_BOOTSTRAP_DATA.common.application_root;
+  // Remove dangerous characters, allow only typical path characters
+  // Remove anything after a '?' or '#' (no query/fragments)
+  let safePath = path.split(/[?#]/)[0];
+  // Disallow dangerous meta characters
+  if (/[^a-zA-Z0-9\/._-]/.test(safePath)) {
+    return DEFAULT_BOOTSTRAP_DATA.common.application_root;
+  }
+  // Ensure starts with /
+  if (!safePath.startsWith('/')) {
+    safePath = '/' + safePath;
+  }
+  // Remove trailing slashes except root
+  if (safePath.length > 1) {
+    safePath = safePath.replace(/\/+$/, '');
+  }
+  return safePath;
+}
+
 export default function getBootstrapData(): BootstrapData {
   if (cachedBootstrapData === null) {
     const appContainer = document.getElementById('app');
     const dataBootstrap = appContainer?.getAttribute('data-bootstrap');
-    cachedBootstrapData = dataBootstrap
-      ? JSON.parse(dataBootstrap)
-      : DEFAULT_BOOTSTRAP_DATA;
+    if (dataBootstrap) {
+      try {
+        const parsed = JSON.parse(dataBootstrap);
+        // Sanitize the application_root immediately after parsing bootstrap data
+        if (
+          parsed &&
+          parsed.common &&
+          typeof parsed.common.application_root === 'string'
+        ) {
+          parsed.common.application_root = sanitizeAppRoot(parsed.common.application_root);
+        }
+        cachedBootstrapData = parsed;
+      } catch (e) {
+        cachedBootstrapData = DEFAULT_BOOTSTRAP_DATA;
+      }
+    } else {
+      cachedBootstrapData = DEFAULT_BOOTSTRAP_DATA;
+    }
   }
   // Add a fallback to ensure the returned value is always of type BootstrapData
   return cachedBootstrapData ?? DEFAULT_BOOTSTRAP_DATA;
EOF
@@ -21,13 +21,49 @@

let cachedBootstrapData: BootstrapData | null = null;

// Helper to sanitize application root paths
function sanitizeAppRoot(path: string): string {
if (typeof path !== 'string') return DEFAULT_BOOTSTRAP_DATA.common.application_root;
// Remove dangerous characters, allow only typical path characters
// Remove anything after a '?' or '#' (no query/fragments)
let safePath = path.split(/[?#]/)[0];
// Disallow dangerous meta characters
if (/[^a-zA-Z0-9\/._-]/.test(safePath)) {
return DEFAULT_BOOTSTRAP_DATA.common.application_root;
}
// Ensure starts with /
if (!safePath.startsWith('/')) {
safePath = '/' + safePath;
}
// Remove trailing slashes except root
if (safePath.length > 1) {
safePath = safePath.replace(/\/+$/, '');
}
return safePath;
}

export default function getBootstrapData(): BootstrapData {
if (cachedBootstrapData === null) {
const appContainer = document.getElementById('app');
const dataBootstrap = appContainer?.getAttribute('data-bootstrap');
cachedBootstrapData = dataBootstrap
? JSON.parse(dataBootstrap)
: DEFAULT_BOOTSTRAP_DATA;
if (dataBootstrap) {
try {
const parsed = JSON.parse(dataBootstrap);
// Sanitize the application_root immediately after parsing bootstrap data
if (
parsed &&
parsed.common &&
typeof parsed.common.application_root === 'string'
) {
parsed.common.application_root = sanitizeAppRoot(parsed.common.application_root);
}
cachedBootstrapData = parsed;
} catch (e) {
cachedBootstrapData = DEFAULT_BOOTSTRAP_DATA;
}
} else {
cachedBootstrapData = DEFAULT_BOOTSTRAP_DATA;
}
}
// Add a fallback to ensure the returned value is always of type BootstrapData
return cachedBootstrapData ?? DEFAULT_BOOTSTRAP_DATA;
Copilot is powered by AI and may make mistakes. Always verify output.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Sep 4, 2025

🎪 Showtime is building environment on GHA for 775063a

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Sep 4, 2025

🎪 Showtime deployed environment on GHA for 775063a

Environment: http://52.37.181.94:8080 (admin/admin)
Lifetime: 24h auto-cleanup
Updates: New commits create fresh environments automatically

@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented Sep 4, 2025

Bito Automatic Review Failed - Technical Failure

Bito encountered technical difficulties while generating code feedback . To retry, type /review in a comment and save. If the issue persists, contact support@bito.ai and provide the following details:

Agent Run ID: cd28ace8-9a50-4f97-9cad-f78ab6f62b18

@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented Sep 4, 2025

Bito Automatic Review Failed - Technical Failure

Bito encountered technical difficulties while generating code feedback . To retry, type /review in a comment and save. If the issue persists, contact support@bito.ai and provide the following details:

Agent Run ID: e35cf26e-49b6-4ff9-a33c-1cf1925825f9

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this change intentional?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

wasn't, now I'm wondering what's not deterministic in our package.json causing these side effects ...

Comment thread superset/datasets/api.py
mistercrunch and others added 13 commits September 17, 2025 21:24
…and Explore paths

Fixes two issues with the drill-through chart configuration:

1. **SelectAsyncControl crash on clear**: Fixed isLabeledValue function to handle null values properly when clearing selections
2. **Missing field in Explore path**: Added drill_through_chart_id to both dataset API response and SqlaTable.data property serialization

This ensures the drill-through chart field loads and saves correctly whether accessed from:
- Datasets CRUD interface
- Explore → Edit Dataset flow

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Create DashboardContextFormData type for explicit dashboard context contract
- Add useDashboardFormData hook to encapsulate complex dashboard filter logic
- Simplify DrillDetailPane from 40+ lines to 4 lines using new hook
- Fix Edit Chart link to open drill-through chart instead of original chart
- Ensure StatefulChart inherits dashboard native filters, colors, and context

This ensures drill-through charts get the same dashboard context as regular
dashboard charts, providing consistent filter inheritance and user experience.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Replace manual URL construction in DrillDetailModal with getExploreUrl utility
- Refactor ChartSelect to derive props from SelectAsyncControl using ComponentProps
- Add comprehensive test coverage for ChartSelect and useDashboardFormData components
- Update DatasourceEditor to use allowClear instead of deprecated clearable prop
- Use rest pattern for better prop forwarding and type safety

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…archParams

Replace manual URL construction with SupersetClient's built-in searchParams support.
Rename queryParams prop to searchParams for API alignment and eliminate 12 lines
of URL building logic.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…vigation

- Only show Edit chart button when drill-through chart is configured
- Fix navigation to properly route to explore page instead of dashboard
- Use Button href for same-tab navigation (no target="_blank")
- Add Dataset.id type definition for URL generation
- Update tests to verify link behavior and required dataset properties

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Make Explore button visible only when drill-through chart is configured
- Change button text from "Edit chart" to "Explore" for clarity
- Fix Explore button to navigate to correct drill-through chart instead of dashboard
- Add proper dashboard context resolution using useDashboardFormData hook
- Use getFormDataWithDashboardContext for proper filter and context mixing
- Simplify StatefulChart rendering with proper formDataOverrides
- Generate explore URLs using getExploreUrl with GET method for reliability
- Add error handling for URL generation with user-friendly toast messages
- Remove duplicate filter conversion logic and use established patterns

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…herited filters

- Mark drill-to-detail filters with `isExtra: true` in `getFormDataWithDashboardContext`
- Ensures context menu filters (e.g., "drill to details by -> Canada") appear as
  dashboard-inherited filters rather than chart-native filters in explore page
- Maintains consistency with filter bar filters which already display correctly
- Updated `getFormDataWithDashboardContext` to accept optional `drillToDetailFilters` parameter

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Simplifies repeated postFormData + mountExploreUrl + dashboard_page_id pattern
used across 6+ files by providing a clean, type-safe async utility.

Benefits:
- DrillDetailModal: 15 lines → 5 lines (67% reduction)
- Centralized URL generation logic for better maintainability
- Type-safe options parameter for chartId, tabId, dashboardPageId
- Comprehensive test coverage with 4 test scenarios
- Better error handling and promise chain management

Files refactored:
- DrillDetailModal: Simplified async URL generation
- DrillByModal: Clean promise chain with proper error handling
- SqlLab ResultSet: Modern async/await pattern
- Added comprehensive utility tests

All component tests passing ✅

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@rusackas
Copy link
Copy Markdown
Member

Any hope of getting this one through? It's a pretty awesome feature :D

@Gunnnn
Copy link
Copy Markdown

Gunnnn commented Nov 26, 2025

What can i do to make this PR happen? Please help me to begin, i need it dreadfully for my work!

@syafiqmuda
Copy link
Copy Markdown

how to enable this function? I got issue with this drill-to-detail table where I can sort the table or resize the row and column width

#32793 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants