Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,18 @@ describe('should collect control values and create SFD', () => {
metrics: formData.standardizedFormData.controls.metrics,
}),
});
// a target viz whose "Time shift" control offers inherit/custom choices
getChartControlPanelRegistry().registerValue('target_viz_inherit', {
controlPanelSections: [
sections.timeComparisonControls({}),
{
label: 'transform controls',
controlSetRows: publicControlFields
.filter(c => c !== 'time_compare')
.map(control => [control]),
},
],
});
});

test('should avoid to overlap', () => {
Expand Down Expand Up @@ -344,6 +356,52 @@ describe('should collect control values and create SFD', () => {
]);
});

test('strips inherit/custom time shifts when target viz does not support them', () => {
// Table/Big Number period-over-period offer "inherit" and "custom" time
// shifts; the timeseries advanced-analytics target (target_viz) does not.
// Carrying them over leaves un-removable tags on the new chart (SC-99170).
const store = {
...sourceMockStore,
form_data: {
...sourceMockFormData,
time_compare: ['1 year ago', 'inherit', 'custom'],
},
};
const sfd = new StandardizedFormData(store.form_data);
const { formData } = sfd.transform('target_viz', store);
// the free-form delta survives, the special markers are dropped
expect(formData.time_compare).toEqual(['1 year ago']);
});

test('preserves inherit/custom time shifts when target viz supports them', () => {
const store = {
...sourceMockStore,
form_data: {
...sourceMockFormData,
time_compare: ['1 year ago', 'inherit'],
},
};
const sfd = new StandardizedFormData(store.form_data);
const { formData } = sfd.transform('target_viz_inherit', store);
expect(formData.time_compare).toEqual(['1 year ago', 'inherit']);
});
Comment on lines +359 to +387

@sadpandajoe sadpandajoe Jun 9, 2026

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.

The new tests only exercise the "some values survive" branch of filtered.length ? filtered : null. Could we add a case where every value is special (e.g. time_compare: ['inherit'] → expect null) so the empty-result path is locked in, along with the time_compare: null serialization that flows out of getFormDataFromControls?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good call. Added a test that converts a chart with time_compare: ['inherit', 'custom'] to a viz without those choices and asserts the value comes out null, so the empty-result branch is locked in.


test('clears time_compare to null when every shift is unsupported', () => {
// When only special markers carry over and none survive, the control value
// must serialize to null (not an empty array) so the target chart shows an
// empty "Time shift" rather than a stray tag (SC-99170).
const store = {
...sourceMockStore,
form_data: {
...sourceMockFormData,
time_compare: ['inherit', 'custom'],
},
};
const sfd = new StandardizedFormData(store.form_data);
const { formData } = sfd.transform('target_viz', store);
expect(formData.time_compare).toBeNull();
});

test('should inherit standardizedFormData and memorizedFormData is LIFO', () => {
// from source_viz to target_viz
const sfd = new StandardizedFormData(sourceMockFormData);
Expand Down
36 changes: 36 additions & 0 deletions superset-frontend/src/explore/controlUtils/standardizedFormData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,41 @@ export class StandardizedFormData {
return controls;
}

// Time shifts only meaningful for viz types whose "Time shift" control offers
// them (Big Number / Table period-over-period). Other viz types reuse the same
// `time_compare` key without these choices.
private static specialTimeShifts = ['inherit', 'custom'];

// Drop `time_compare` markers the target viz can't honor so they don't carry
// over as un-removable tags when switching chart types.
static dropUnsupportedTimeShifts(
controlsState: Record<string, unknown>,
): void {
const control = controlsState?.time_compare as
| { value?: unknown; choices?: unknown }
| undefined;
if (!control || !Array.isArray(control.value)) {
return;
}
const supportedChoices = new Set(
ensureIsArray(control.choices)
.filter(
(choice): choice is [string, string] =>
Array.isArray(choice) && typeof choice[0] === 'string',
)
.map(choice => choice[0]),
);
const filtered = control.value.filter(
(shift: unknown) =>
typeof shift !== 'string' ||
!StandardizedFormData.specialTimeShifts.includes(shift) ||
supportedChoices.has(shift),
);
if (filtered.length !== control.value.length) {
control.value = filtered.length ? filtered : null;
}
}

private getLatestFormData(vizType: string): QueryFormData {
if (this.has(vizType)) {
return this.get(vizType);
Expand Down Expand Up @@ -215,6 +250,7 @@ export class StandardizedFormData {
...publicFormData,
viz_type: targetVizType,
});
StandardizedFormData.dropUnsupportedTimeShifts(targetControlsState);
const targetFormData = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...getFormDataFromControls(targetControlsState as any),
Expand Down
Loading