diff --git a/superset-frontend/src/explore/controlUtils/standardizedFormData.test.ts b/superset-frontend/src/explore/controlUtils/standardizedFormData.test.ts index e0f770055510..9c2dc91d0b8d 100644 --- a/superset-frontend/src/explore/controlUtils/standardizedFormData.test.ts +++ b/superset-frontend/src/explore/controlUtils/standardizedFormData.test.ts @@ -217,6 +217,8 @@ describe('should collect control values and create SFD', () => { // advanced analytics - resample resample_rule: '1D', resample_method: 'zerofill', + // dashboard context + dashboardId: 123, }; const sourceMockFormData: QueryFormData = { ...sharedControlsFormData, @@ -240,12 +242,15 @@ describe('should collect control values and create SFD', () => { }; beforeAll(() => { + // dashboardId is not a control, it's just context, so exclude it from control definitions + const publicControlFields = publicControls.filter(c => c !== 'dashboardId'); + getChartControlPanelRegistry().registerValue('source_viz', { controlPanelSections: [ sections.advancedAnalyticsControls, { label: 'transform controls', - controlSetRows: publicControls.map(control => [control]), + controlSetRows: publicControlFields.map(control => [control]), }, { label: 'axis column', @@ -258,7 +263,7 @@ describe('should collect control values and create SFD', () => { sections.advancedAnalyticsControls, { label: 'transform controls', - controlSetRows: publicControls.map(control => [control]), + controlSetRows: publicControlFields.map(control => [control]), }, { label: 'axis column', @@ -447,6 +452,83 @@ describe('should transform form_data between table and bigNumberTotal', () => { ]); expect(tblFormData.groupby).toEqual(['name', 'gender', adhocColumn]); }); + + test('preserves dashboardId when transforming between viz types', () => { + // Create form data with dashboardId (simulating opening explore from a dashboard) + const formDataWithDashboard = { + ...tableVizFormData, + dashboardId: 42, + }; + const storeWithDashboard = { + ...tableVizStore, + form_data: formDataWithDashboard, + }; + + // Transform table -> bigNumberTotal + const sfd = new StandardizedFormData(formDataWithDashboard); + const { formData: bntFormData } = sfd.transform( + VizType.BigNumberTotal, + storeWithDashboard, + ); + + // Verify dashboardId is preserved after transformation + expect(bntFormData.dashboardId).toBe(42); + + // Transform back bigNumberTotal -> table + const sfd2 = new StandardizedFormData(bntFormData); + const { formData: tblFormData } = sfd2.transform('table', { + ...storeWithDashboard, + form_data: bntFormData, + controls: { + ...tableVizStore.controls, + }, + }); + + // Verify dashboardId is still preserved after second transformation + expect(tblFormData.dashboardId).toBe(42); + }); + + test('handles missing dashboardId gracefully', () => { + // Test with no dashboardId (exploring a chart not from a dashboard) + const formDataNoDashboard = { + ...tableVizFormData, + // dashboardId is undefined + }; + const storeNoDashboard = { + ...tableVizStore, + form_data: formDataNoDashboard, + }; + + const sfd = new StandardizedFormData(formDataNoDashboard); + const { formData: bntFormData } = sfd.transform( + VizType.BigNumberTotal, + storeNoDashboard, + ); + + // dashboardId should not be present when it was never set + expect(bntFormData.dashboardId).toBeUndefined(); + }); + + test('handles null dashboardId', () => { + // Test with explicit null dashboardId + const formDataNullDashboard = { + ...tableVizFormData, + dashboardId: null, + }; + const storeNullDashboard = { + ...tableVizStore, + form_data: formDataNullDashboard, + }; + + const sfd = new StandardizedFormData(formDataNullDashboard); + const { formData: bntFormData } = sfd.transform( + VizType.BigNumberTotal, + storeNullDashboard, + ); + + // null is falsy, so dashboardId should not be added + expect(bntFormData.dashboardId).toBeUndefined(); + }); }); // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks diff --git a/superset-frontend/src/explore/controlUtils/standardizedFormData.ts b/superset-frontend/src/explore/controlUtils/standardizedFormData.ts index 73798989c91d..6bdf4a263bd0 100644 --- a/superset-frontend/src/explore/controlUtils/standardizedFormData.ts +++ b/superset-frontend/src/explore/controlUtils/standardizedFormData.ts @@ -81,6 +81,8 @@ export const publicControls = [ // advanced analytics - resample 'resample_rule', // via sections.advancedAnalytics 'resample_method', // via sections.advancedAnalytics + // dashboard context + 'dashboardId', // preserve dashboard context when changing viz type ]; export class StandardizedFormData { @@ -216,6 +218,10 @@ export class StandardizedFormData { }); const targetFormData = { ...getFormDataFromControls(targetControlsState), + // Preserve dashboard context when switching viz types. + ...(publicFormData.dashboardId && { + dashboardId: publicFormData.dashboardId, + }), standardizedFormData: this.serialize(), }; diff --git a/superset_text.yml b/superset_text.yml index a253d63da8a1..3b3a346b54f9 100644 --- a/superset_text.yml +++ b/superset_text.yml @@ -16,7 +16,7 @@ # under the License. -# To set the images of your preferred database, you may create a mapping here with engine and locations of the relevant images. The image can be hosted locally inside your static/file directory or online (e.g. S3) +# To set the images of your preferred database, you may create a mapping here with engine and locations of the relevant images. The image can be hosted locally inside your static/file directory or online (e.g. S3). # DB_IMAGES: # postgresql: "path/to/image/postgres.jpg"