Compare commits

...

2 Commits

Author SHA1 Message Date
geido
4441e9c7ee [sc-94612] yellow override icon on Time Grain (shared controls)
Modern ECharts charts pull time_grain_sqla from the shared control
in @superset-ui/chart-controls, not the legacy src/explore/controls.tsx.
Surface the same dashboard-override warning there so the yellow icon
appears on those charts when the time grain is inherited from a dashboard.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-16 18:24:28 +00:00
geido
6d90789613 feat(explore): yellow icon on Time Grain & Time Column when overridden by dashboard [sc-94612]
Extends the existing dashboard-override indicator (currently only on Color
scheme) to the time_grain_sqla and granularity_sqla controls. getFormData-
WithDashboardContext now records dashboard_time_grain_sqla /
dashboard_granularity_sqla flags when those values come from the dashboard
context, and the controls surface a warning tooltip + icon via ControlHeader.

First slice of sc-94612. Multi-layer (deck_multi) layer indicators with
eye-toggle/remove controls still remain.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-16 08:54:07 +00:00
4 changed files with 70 additions and 5 deletions

View File

@@ -180,6 +180,13 @@ const granularity: SharedControlConfig<'SelectControl'> = {
sortComparator: () => 0, // Disable frontend sorting to preserve backend order
};
// Surfaced on the Time Grain header when the value is inherited from the
// dashboard the chart was opened from (see getFormDataWithDashboardContext).
const DASHBOARD_TIME_GRAIN_ALERT = t(
'The time grain is determined by the related dashboard. ' +
'Open this chart outside of the dashboard to change it.',
);
const time_grain_sqla: SharedControlConfig<'SelectControl'> = {
type: 'SelectControl',
label: TIME_FILTER_LABELS.time_grain_sqla,
@@ -201,8 +208,12 @@ const time_grain_sqla: SharedControlConfig<'SelectControl'> = {
'grain is the time interval represented by a ' +
'single point on the chart.',
),
mapStateToProps: ({ datasource }) => ({
mapStateToProps: ({ datasource, form_data }) => ({
choices: (datasource as Dataset)?.time_grain_sqla || [],
warning:
form_data?.dashboardId && isDefined(form_data?.dashboard_time_grain_sqla)
? DASHBOARD_TIME_GRAIN_ALERT
: null,
}),
visibility: displayTimeRelatedControls,
sortComparator: () => 0, // Disable frontend sorting to preserve backend order

View File

@@ -154,6 +154,8 @@ const getExpectedResultFormData = (overrides: JsonObject = {}) => ({
shared_label_colors: ['boy', 'girl'],
own_color_scheme: 'supersetColors',
dashboard_color_scheme: 'd3Category20b',
dashboard_time_grain_sqla: 'P1D',
dashboard_granularity_sqla: 'ds',
extra_filters: [
{
col: '__time_range',

View File

@@ -243,6 +243,21 @@ export const getFormDataWithDashboardContext = (
const dashboardColorScheme = dashboardContextFormData.color_scheme;
const appliedColorScheme = dashboardColorScheme || ownColorScheme;
// Track time controls overridden by the dashboard so Explore can flag them
// with a warning icon (see time_grain_sqla / granularity_sqla controls).
const dashboardTimeGrain =
filterBoxData.time_grain_sqla ?? nativeFiltersData.time_grain_sqla;
const dashboardTimeColumn =
filterBoxData.granularity_sqla ?? nativeFiltersData.granularity_sqla;
const dashboardTimeOverrides: JsonObject = {
...(isDefined(dashboardTimeGrain) && {
dashboard_time_grain_sqla: dashboardTimeGrain,
}),
...(isDefined(dashboardTimeColumn) && {
dashboard_granularity_sqla: dashboardTimeColumn,
}),
};
const deckGLProperties: JsonObject = {};
if (
@@ -301,5 +316,6 @@ export const getFormDataWithDashboardContext = (
color_scheme: appliedColorScheme,
dashboard_color_scheme: dashboardColorScheme,
...deckGLProperties,
...dashboardTimeOverrides,
};
};

View File

@@ -60,6 +60,7 @@ import type { Column, SequentialScheme } from '@superset-ui/core';
import {
getCategoricalSchemeRegistry,
getSequentialSchemeRegistry,
isDefined,
legacyValidateInteger,
validateNonEmpty,
} from '@superset-ui/core';
@@ -80,8 +81,26 @@ interface Datasource {
interface ControlState {
datasource?: Datasource;
controls?: Record<string, { value?: unknown }>;
form_data?: {
dashboardId?: number;
dashboard_time_grain_sqla?: unknown;
dashboard_granularity_sqla?: unknown;
[key: string]: unknown;
};
}
// Shown next to time controls in Explore when the value was overridden by the
// dashboard the chart was opened from (see getFormDataWithDashboardContext).
const DASHBOARD_TIME_GRAIN_ALERT = t(
'The time grain is determined by the related dashboard. ' +
'Open this chart outside of the dashboard to change it.',
);
const DASHBOARD_TIME_COLUMN_ALERT = t(
'The time column is determined by the related dashboard. ' +
'Open this chart outside of the dashboard to change it.',
);
interface ControlConfig {
includeTime?: boolean;
[key: string]: unknown;
@@ -324,7 +343,11 @@ export const controls = {
optionRenderer: (c: Column) => <StyledColumnOption column={c} showType />,
valueKey: 'column_name',
mapStateToProps: (state: ControlState) => {
const props: { choices?: Column[]; default?: string | null } = {};
const props: {
choices?: Column[];
default?: string | null;
warning?: string | null;
} = {};
if (state.datasource) {
props.choices = state.datasource.granularity_sqla;
props.default = null;
@@ -334,6 +357,12 @@ export const controls = {
props.default = props.choices[0].column_name;
}
}
const { dashboardId, dashboard_granularity_sqla } =
state.form_data ?? {};
props.warning =
dashboardId && isDefined(dashboard_granularity_sqla)
? DASHBOARD_TIME_COLUMN_ALERT
: null;
return props;
},
},
@@ -349,9 +378,16 @@ export const controls = {
'The options here are defined on a per database ' +
'engine basis in the Superset source code.',
),
mapStateToProps: (state: ControlState) => ({
choices: state.datasource ? state.datasource.time_grain_sqla : null,
}),
mapStateToProps: (state: ControlState) => {
const { dashboardId, dashboard_time_grain_sqla } = state.form_data ?? {};
return {
choices: state.datasource ? state.datasource.time_grain_sqla : null,
warning:
dashboardId && isDefined(dashboard_time_grain_sqla)
? DASHBOARD_TIME_GRAIN_ALERT
: null,
};
},
},
time_range: {