This is an automated email from the ASF dual-hosted git repository.

beto pushed a commit to branch dbt-metricflow
in repository https://gitbox.apache.org/repos/asf/superset.git

commit a2c885659231ac6112964b82805363591e177883
Author: Beto Dealmeida <robe...@dealmeida.net>
AuthorDate: Thu Jul 17 22:20:19 2025 -0400

    Checkpoint
---
 .../src/shared-controls/dndControls.tsx            |  18 +-
 .../plugin-chart-table/src/controlPanel.tsx        |  24 ++-
 .../DndColumnSelectControl/ColumnSelectPopover.tsx | 227 ++++++++++++---------
 .../MetricControl/AdhocMetricEditPopover/index.jsx | 114 ++++++++---
 .../controls/SemanticLayerVerification.tsx         | 105 ++++++----
 .../components/controls/withAsyncVerification.tsx  |  20 +-
 6 files changed, 323 insertions(+), 185 deletions(-)

diff --git 
a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx
 
b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx
index 1056a5414f..25140fdbc4 100644
--- 
a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx
+++ 
b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx
@@ -75,7 +75,7 @@ export function setSemanticLayerUtilities(utilities: {
     createSemanticLayerOnChange,
     SEMANTIC_LAYER_CONTROL_FIELDS,
   } = utilities);
-  
+
   // Notify all enhanced controls that utilities are now available
   enhancedControls.forEach(control => {
     control.invalidateCache();
@@ -106,16 +106,16 @@ function enhanceControlWithSemanticLayer(
   // Cache for the enhanced control type
   let cachedEnhancedType: any = null;
   let utilitiesWereAvailable = false;
-  
+
   // Register with notification system
   enhancedControls.push({
     controlName,
     invalidateCache: () => {
       cachedEnhancedType = null;
       utilitiesWereAvailable = false;
-    }
+    },
   });
-  
+
   // Return a control that will be enhanced at runtime if utilities are 
available
   return {
     ...baseControl,
@@ -123,7 +123,7 @@ function enhanceControlWithSemanticLayer(
     get type() {
       // Check if utilities became available since last call
       const utilitiesAvailableNow = !!withAsyncVerification;
-      
+
       if (utilitiesAvailableNow) {
         // If utilities just became available or we haven't cached yet, create 
enhanced control
         if (!utilitiesWereAvailable || !cachedEnhancedType) {
@@ -131,7 +131,7 @@ function enhanceControlWithSemanticLayer(
             verificationType === 'metrics'
               ? createMetricsVerification(controlName)
               : createColumnsVerification(controlName);
-          
+
           cachedEnhancedType = withAsyncVerification({
             baseControl: baseControl.type,
             verify: verificationFn,
@@ -141,13 +141,13 @@ function enhanceControlWithSemanticLayer(
             ),
             showLoadingState: true,
           });
-          
+
           utilitiesWereAvailable = true;
         }
-        
+
         return cachedEnhancedType;
       }
-      
+
       utilitiesWereAvailable = false;
       return baseControl.type;
     },
diff --git a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx 
b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx
index 86bdcc6f3e..bc0ce2afcb 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx
@@ -311,23 +311,29 @@ const config: ControlPanelConfig = {
                   sharedControls?.metrics?.mapStateToProps;
                 const newState =
                   originalMapStateToProps?.(state, controlState) ?? {};
-                
+
                 // Add table-specific props while preserving semantic layer 
enhancements
-                newState.columns = 
datasource?.columns[0]?.hasOwnProperty('filterable')
+                newState.columns = datasource?.columns[0]?.hasOwnProperty(
+                  'filterable',
+                )
                   ? (datasource as Dataset)?.columns?.filter(
                       (c: ColumnMeta) => c.filterable,
                     )
                   : datasource?.columns;
                 newState.savedMetrics = defineSavedMetrics(datasource);
-                newState.selectedMetrics = form_data.metrics ||
+                newState.selectedMetrics =
+                  form_data.metrics ||
                   (form_data.metric ? [form_data.metric] : []);
                 newState.datasource = datasource;
-                newState.externalValidationErrors = 
validateAggControlValues(controls, [
-                  controls.groupby?.value,
-                  controls.percent_metrics?.value,
-                  controlState.value,
-                ]);
-                
+                newState.externalValidationErrors = validateAggControlValues(
+                  controls,
+                  [
+                    controls.groupby?.value,
+                    controls.percent_metrics?.value,
+                    controlState.value,
+                  ],
+                );
+
                 return newState;
               },
               rerender: ['groupby', 'percent_metrics'],
diff --git 
a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx
 
b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx
index 952b0490f3..6e0bed1b2e 100644
--- 
a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx
+++ 
b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx
@@ -51,9 +51,9 @@ import {
 import sqlKeywords from 'src/SqlLab/utils/sqlKeywords';
 import { getColumnKeywords } from 'src/explore/controlUtils/getColumnKeywords';
 import { StyledColumnOption } from 'src/explore/components/optionRenderers';
-import { 
+import {
   collectQueryFields,
-  callValidationAPI 
+  callValidationAPI,
 } from 'src/explore/components/controls/SemanticLayerVerification';
 import {
   POPOVER_INITIAL_HEIGHT,
@@ -132,15 +132,17 @@ const ColumnSelectPopover = ({
     state => state.explore.form_data,
   );
   const store = useStore();
-  
+
   // Check if this is a semantic layer dataset
   const isSemanticLayer = useMemo(() => {
     if (!datasource || !('database' in datasource) || !datasource.database) {
       return false;
     }
-    return 
Boolean(datasource.database.engine_information?.supports_dynamic_columns);
+    return Boolean(
+      datasource.database.engine_information?.supports_dynamic_columns,
+    );
   }, [datasource]);
-  
+
   // For semantic layers, disable Saved and Custom SQL tabs
   const effectiveDisabledTabs = useMemo(() => {
     const tabs = new Set(disabledTabs);
@@ -150,7 +152,7 @@ const ColumnSelectPopover = ({
     }
     return tabs;
   }, [disabledTabs, isSemanticLayer]);
-  
+
   const [initialLabel] = useState(label);
   const [initialAdhocColumn, initialCalculatedColumn, initialSimpleColumn] =
     getInitialColumnValues(editedColumn);
@@ -166,7 +168,8 @@ const ColumnSelectPopover = ({
   >(initialSimpleColumn);
   const [selectedTab, setSelectedTab] = useState<string | null>(null);
   const [validDimensions, setValidDimensions] = useState<string[] | 
null>(null);
-  const [isLoadingValidDimensions, setIsLoadingValidDimensions] = 
useState(false);
+  const [isLoadingValidDimensions, setIsLoadingValidDimensions] =
+    useState(false);
   const previousFormDataRef = useRef<string>('');
 
   const [resizeButton, width, height] = useResizeButton(
@@ -176,34 +179,31 @@ const ColumnSelectPopover = ({
 
   const sqlEditorRef = useRef(null);
 
-  const [calculatedColumns, simpleColumns] = useMemo(
-    () => {
-      // Use columns from Redux datasource state (which includes disabled 
states) instead of props
-      const columnsToUse = datasource?.columns || columns || [];
-      
-      const [calculated, simple] = columnsToUse.reduce(
-        (acc: [ColumnMeta[], ColumnMeta[]], column: ColumnMeta) => {
-          if (column.expression) {
-            acc[0].push(column);
-          } else {
-            acc[1].push(column);
-          }
-          return acc;
-        },
-        [[], []],
-      ) || [[], []];
-
-      // For semantic layer datasets, filter simple columns to show only valid 
dimensions
-      // Use the isDisabled state set by the main verification system instead 
of separate API calls
-      if (isSemanticLayer) {
-        const filteredSimple = simple.filter(column => !column.isDisabled);
-        return [calculated, filteredSimple];
-      }
+  const [calculatedColumns, simpleColumns] = useMemo(() => {
+    // Use columns from Redux datasource state (which includes disabled 
states) instead of props
+    const columnsToUse = datasource?.columns || columns || [];
 
-      return [calculated, simple];
-    },
-    [datasource?.columns, columns, isSemanticLayer],
-  );
+    const [calculated, simple] = columnsToUse.reduce(
+      (acc: [ColumnMeta[], ColumnMeta[]], column: ColumnMeta) => {
+        if (column.expression) {
+          acc[0].push(column);
+        } else {
+          acc[1].push(column);
+        }
+        return acc;
+      },
+      [[], []],
+    ) || [[], []];
+
+    // For semantic layer datasets, filter simple columns to show only valid 
dimensions
+    // Use the isDisabled state set by the main verification system instead of 
separate API calls
+    if (isSemanticLayer) {
+      const filteredSimple = simple.filter(column => !column.isDisabled);
+      return [calculated, filteredSimple];
+    }
+
+    return [calculated, simple];
+  }, [datasource?.columns, columns, isSemanticLayer]);
 
   const onSqlExpressionChange = useCallback(
     sqlExpression => {
@@ -249,7 +249,7 @@ const ColumnSelectPopover = ({
     if (isSemanticLayer) {
       return TABS_KEYS.SIMPLE;
     }
-    
+
     // Original logic for non-semantic layer datasets
     return initialAdhocColumn
       ? TABS_KEYS.SQL_EXPRESSION
@@ -272,42 +272,57 @@ const ColumnSelectPopover = ({
     console.log('datasource exists:', !!datasource);
     console.log('selectedTab:', selectedTab);
     console.log('TABS_KEYS.SIMPLE:', TABS_KEYS.SIMPLE);
-    console.log('Should trigger API?', isSemanticLayer && formData && 
datasource && 
-        (selectedTab === TABS_KEYS.SIMPLE || selectedTab === null));
-    
+    console.log(
+      'Should trigger API?',
+      isSemanticLayer &&
+        formData &&
+        datasource &&
+        (selectedTab === TABS_KEYS.SIMPLE || selectedTab === null),
+    );
+
     // Disable column modal API calls - semantic layer verification handles 
disabled states automatically
-    if (false && isSemanticLayer && formData && datasource && 
-        (selectedTab === TABS_KEYS.SIMPLE || selectedTab === null)) {
-      
+    if (
+      false &&
+      isSemanticLayer &&
+      formData &&
+      datasource &&
+      (selectedTab === TABS_KEYS.SIMPLE || selectedTab === null)
+    ) {
       const fetchValidDimensions = async () => {
         setIsLoadingValidDimensions(true);
-        
+
         try {
           // Use the same 50ms delay that fixed the main verification timing 
issue
           await new Promise(resolve => setTimeout(resolve, 50));
-          
+
           // Get the most current form data from store
           const currentState = store.getState() as ExplorePageState;
           let currentFormData = currentState.explore.form_data;
-          
+
           // If we're in a table and don't have metrics/dimensions, try to get 
from controls state
-          if ((!currentFormData.metrics && !currentFormData.groupby && 
!currentFormData.all_columns) ||
-              (Array.isArray(currentFormData.metrics) && 
currentFormData.metrics.length === 0 &&
-               Array.isArray(currentFormData.groupby) && 
currentFormData.groupby.length === 0)) {
-            
+          if (
+            (!currentFormData.metrics &&
+              !currentFormData.groupby &&
+              !currentFormData.all_columns) ||
+            (Array.isArray(currentFormData.metrics) &&
+              currentFormData.metrics.length === 0 &&
+              Array.isArray(currentFormData.groupby) &&
+              currentFormData.groupby.length === 0)
+          ) {
             // Try to get from the controls state instead
             const controlsState = (currentState as any).explore?.controls;
             if (controlsState) {
               const enhancedFormData = { ...currentFormData };
-              
+
               // Get metrics from controls
               if (controlsState.metrics?.value) {
                 enhancedFormData.metrics = controlsState.metrics.value;
               }
               if (controlsState.percent_metrics?.value) {
-                enhancedFormData.percent_metrics = 
controlsState.percent_metrics.value;
+                enhancedFormData.percent_metrics =
+                  controlsState.percent_metrics.value;
               }
-              
+
               // Get dimensions from controls
               if (controlsState.groupby?.value) {
                 enhancedFormData.groupby = controlsState.groupby.value;
@@ -315,25 +330,48 @@ const ColumnSelectPopover = ({
               if (controlsState.all_columns?.value) {
                 enhancedFormData.all_columns = controlsState.all_columns.value;
               }
-              
+
               console.log('=== ENHANCED FORM DATA FROM CONTROLS ===');
-              console.log('Controls state metrics:', 
controlsState.metrics?.value);
-              console.log('Controls state groupby:', 
controlsState.groupby?.value);
-              console.log('Controls state all_columns:', 
controlsState.all_columns?.value);
+              console.log(
+                'Controls state metrics:',
+                controlsState.metrics?.value,
+              );
+              console.log(
+                'Controls state groupby:',
+                controlsState.groupby?.value,
+              );
+              console.log(
+                'Controls state all_columns:',
+                controlsState.all_columns?.value,
+              );
               console.log('Enhanced form data:', enhancedFormData);
-              
+
               currentFormData = enhancedFormData;
             }
           }
-          
+
           console.log('=== COLUMN MODAL DEBUG ===');
-          console.log('Column modal Redux state keys:', 
Object.keys(currentFormData));
+          console.log(
+            'Column modal Redux state keys:',
+            Object.keys(currentFormData),
+          );
           console.log('Column modal form data:', currentFormData);
-          console.log('Column modal has metrics?', 'metrics' in 
currentFormData, currentFormData.metrics);
-          console.log('Column modal has groupby?', 'groupby' in 
currentFormData, currentFormData.groupby);
-          console.log('Column modal collected query fields:', 
collectQueryFields(currentFormData));
+          console.log(
+            'Column modal has metrics?',
+            'metrics' in currentFormData,
+            currentFormData.metrics,
+          );
+          console.log(
+            'Column modal has groupby?',
+            'groupby' in currentFormData,
+            currentFormData.groupby,
+          );
+          console.log(
+            'Column modal collected query fields:',
+            collectQueryFields(currentFormData),
+          );
           console.log('=== END COLUMN MODAL DEBUG ===');
-          
+
           const queryFields = collectQueryFields(currentFormData);
           const validationResult = await callValidationAPI(
             datasource,
@@ -352,37 +390,36 @@ const ColumnSelectPopover = ({
           setIsLoadingValidDimensions(false);
         }
       };
-      
+
       // Trigger API call after a delay to ensure state is current
       const timeoutId = setTimeout(() => {
         fetchValidDimensions();
       }, 100);
-      
+
       return () => clearTimeout(timeoutId);
-    } else {
-      setValidDimensions(null);
-      setIsLoadingValidDimensions(false);
     }
+    setValidDimensions(null);
+    setIsLoadingValidDimensions(false);
   }, [isSemanticLayer, selectedTab, datasource, store]);
-  
+
   // Also trigger when form data changes (for subsequent updates)
   useEffect(() => {
     if (isSemanticLayer && validDimensions !== null && formData && datasource) 
{
       const currentFormDataString = JSON.stringify(formData);
-      
+
       // Only make API call if form data actually changed and we already have 
loaded once
       if (currentFormDataString !== previousFormDataRef.current) {
         previousFormDataRef.current = currentFormDataString;
-        
+
         const fetchValidDimensions = async () => {
           setIsLoadingValidDimensions(true);
-          
+
           try {
             await new Promise(resolve => setTimeout(resolve, 50));
-            
+
             const currentState = store.getState() as ExplorePageState;
             const currentFormData = currentState.explore.form_data;
-            
+
             const queryFields = collectQueryFields(currentFormData);
             const validationResult = await callValidationAPI(
               datasource,
@@ -398,7 +435,7 @@ const ColumnSelectPopover = ({
             setIsLoadingValidDimensions(false);
           }
         };
-        
+
         setTimeout(() => {
           fetchValidDimensions();
         }, 50);
@@ -517,15 +554,18 @@ const ColumnSelectPopover = ({
         items={[
           {
             key: TABS_KEYS.SAVED,
-            label: isSemanticLayer && effectiveDisabledTabs.has('saved') ? (
-              <Tooltip
-                title={t('Saved expressions are not supported for semantic 
layer datasets')}
-              >
-                {t('Saved')}
-              </Tooltip>
-            ) : (
-              t('Saved')
-            ),
+            label:
+              isSemanticLayer && effectiveDisabledTabs.has('saved') ? (
+                <Tooltip
+                  title={t(
+                    'Saved expressions are not supported for semantic layer 
datasets',
+                  )}
+                >
+                  {t('Saved')}
+                </Tooltip>
+              ) : (
+                t('Saved')
+              ),
             disabled: effectiveDisabledTabs.has('saved'),
             children: (
               <>
@@ -662,15 +702,18 @@ const ColumnSelectPopover = ({
           },
           {
             key: TABS_KEYS.SQL_EXPRESSION,
-            label: isSemanticLayer && 
effectiveDisabledTabs.has('sqlExpression') ? (
-              <Tooltip
-                title={t('Custom SQL expressions are not supported for 
semantic layer datasets')}
-              >
-                {t('Custom SQL')}
-              </Tooltip>
-            ) : (
-              t('Custom SQL')
-            ),
+            label:
+              isSemanticLayer && effectiveDisabledTabs.has('sqlExpression') ? (
+                <Tooltip
+                  title={t(
+                    'Custom SQL expressions are not supported for semantic 
layer datasets',
+                  )}
+                >
+                  {t('Custom SQL')}
+                </Tooltip>
+              ) : (
+                t('Custom SQL')
+              ),
             disabled: effectiveDisabledTabs.has('sqlExpression'),
             children: (
               <>
diff --git 
a/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.jsx
 
b/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.jsx
index 82d498da7c..f6bc84a8f0 100644
--- 
a/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.jsx
+++ 
b/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.jsx
@@ -19,6 +19,7 @@
 /* eslint-disable camelcase */
 import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
 import {
   isDefined,
   t,
@@ -68,6 +69,8 @@ const propTypes = {
   datasource: PropTypes.object,
   isNewMetric: PropTypes.bool,
   isLabelModified: PropTypes.bool,
+  // Props from Redux
+  reduxDatasource: PropTypes.object,
 };
 
 const defaultProps = {
@@ -90,7 +93,7 @@ const StyledSelect = styled(Select)`
 
 export const SAVED_TAB_KEY = 'SAVED';
 
-export default class AdhocMetricEditPopover extends PureComponent {
+class AdhocMetricEditPopover extends PureComponent {
   // "Saved" is a default tab unless there are no saved metrics for dataset
   defaultActiveTabKey = this.getDefaultTab();
 
@@ -149,16 +152,19 @@ export default class AdhocMetricEditPopover extends 
PureComponent {
   getDefaultTab() {
     const { adhocMetric, savedMetric, savedMetricsOptions, isNewMetric } =
       this.props;
-    
+
     // For semantic layer datasets, always default to Saved tab if available
     if (this.isSemanticLayer()) {
-      if (Array.isArray(savedMetricsOptions) && savedMetricsOptions.length > 
0) {
+      if (
+        Array.isArray(savedMetricsOptions) &&
+        savedMetricsOptions.length > 0
+      ) {
         return SAVED_TAB_KEY;
       }
       // If no saved metrics available, still return SAVED_TAB_KEY to show 
empty state
       return SAVED_TAB_KEY;
     }
-    
+
     if (isDefined(adhocMetric.column) || isDefined(adhocMetric.sqlExpression)) 
{
       return adhocMetric.expressionType;
     }
@@ -177,7 +183,9 @@ export default class AdhocMetricEditPopover extends 
PureComponent {
     if (!datasource || !('database' in datasource) || !datasource.database) {
       return false;
     }
-    return 
Boolean(datasource.database.engine_information?.supports_dynamic_columns);
+    return Boolean(
+      datasource.database.engine_information?.supports_dynamic_columns,
+    );
   }
 
   onSave() {
@@ -324,11 +332,29 @@ export default class AdhocMetricEditPopover extends 
PureComponent {
       datasource,
       isNewMetric,
       isLabelModified,
+      reduxDatasource,
       ...popoverProps
     } = this.props;
     const { adhocMetric, savedMetric } = this.state;
     const keywords = sqlKeywords.concat(getColumnKeywords(columns));
 
+    // For semantic layer datasets, filter saved metrics to show only valid 
ones
+    // Use the isDisabled state set by the main verification system instead of 
all metrics
+    let filteredSavedMetricsOptions = savedMetricsOptions;
+    if (this.isSemanticLayer() && reduxDatasource?.metrics) {
+      // Create a set of metric names that are NOT disabled in Redux state
+      const validMetricNames = new Set(
+        reduxDatasource.metrics
+          .filter(metric => !metric.isDisabled)
+          .map(metric => metric.metric_name),
+      );
+
+      // Filter savedMetricsOptions to only include valid metrics
+      filteredSavedMetricsOptions = ensureIsArray(savedMetricsOptions).filter(
+        metric => validMetricNames.has(metric.metric_name),
+      );
+    }
+
     const columnValue =
       (adhocMetric.column && adhocMetric.column.column_name) ||
       adhocMetric.inferSqlExpressionColumn();
@@ -354,7 +380,10 @@ export default class AdhocMetricEditPopover extends 
PureComponent {
 
     const savedSelectProps = {
       ariaLabel: t('Select saved metrics'),
-      placeholder: t('%s saved metric(s)', savedMetricsOptions?.length ?? 0),
+      placeholder: t(
+        '%s saved metric(s)',
+        filteredSavedMetricsOptions?.length ?? 0,
+      ),
       value: savedMetric?.metric_name,
       onChange: this.onSavedMetricChange,
       allowClear: true,
@@ -399,10 +428,10 @@ export default class AdhocMetricEditPopover extends 
PureComponent {
               key: SAVED_TAB_KEY,
               label: t('Saved'),
               children:
-                ensureIsArray(savedMetricsOptions).length > 0 ? (
+                ensureIsArray(filteredSavedMetricsOptions).length > 0 ? (
                   <FormItem label={t('Saved metric')}>
                     <StyledSelect
-                      options={ensureIsArray(savedMetricsOptions).map(
+                      options={ensureIsArray(filteredSavedMetricsOptions).map(
                         savedMetric => ({
                           value: savedMetric.metric_name,
                           label: this.renderMetricOption(savedMetric),
@@ -446,19 +475,24 @@ export default class AdhocMetricEditPopover extends 
PureComponent {
             },
             {
               key: EXPRESSION_TYPES.SIMPLE,
-              label: extra.disallow_adhoc_metrics || this.isSemanticLayer() ? (
-                <Tooltip
-                  title={
-                    this.isSemanticLayer()
-                      ? t('Simple ad-hoc metrics are not supported for 
semantic layer datasets')
-                      : t('Simple ad-hoc metrics are not enabled for this 
dataset')
-                  }
-                >
-                  {t('Simple')}
-                </Tooltip>
-              ) : (
-                t('Simple')
-              ),
+              label:
+                extra.disallow_adhoc_metrics || this.isSemanticLayer() ? (
+                  <Tooltip
+                    title={
+                      this.isSemanticLayer()
+                        ? t(
+                            'Simple ad-hoc metrics are not supported for 
semantic layer datasets',
+                          )
+                        : t(
+                            'Simple ad-hoc metrics are not enabled for this 
dataset',
+                          )
+                    }
+                  >
+                    {t('Simple')}
+                  </Tooltip>
+                ) : (
+                  t('Simple')
+                ),
               disabled: extra.disallow_adhoc_metrics || this.isSemanticLayer(),
               children: (
                 <>
@@ -487,19 +521,24 @@ export default class AdhocMetricEditPopover extends 
PureComponent {
             },
             {
               key: EXPRESSION_TYPES.SQL,
-              label: extra.disallow_adhoc_metrics || this.isSemanticLayer() ? (
-                <Tooltip
-                  title={
-                    this.isSemanticLayer()
-                      ? t('Custom SQL ad-hoc metrics are not supported for 
semantic layer datasets')
-                      : t('Custom SQL ad-hoc metrics are not enabled for this 
dataset')
-                  }
-                >
-                  {t('Custom SQL')}
-                </Tooltip>
-              ) : (
-                t('Custom SQL')
-              ),
+              label:
+                extra.disallow_adhoc_metrics || this.isSemanticLayer() ? (
+                  <Tooltip
+                    title={
+                      this.isSemanticLayer()
+                        ? t(
+                            'Custom SQL ad-hoc metrics are not supported for 
semantic layer datasets',
+                          )
+                        : t(
+                            'Custom SQL ad-hoc metrics are not enabled for 
this dataset',
+                          )
+                    }
+                  >
+                    {t('Custom SQL')}
+                  </Tooltip>
+                ) : (
+                  t('Custom SQL')
+                ),
               disabled: extra.disallow_adhoc_metrics || this.isSemanticLayer(),
               children: (
                 <SQLEditor
@@ -558,3 +597,10 @@ export default class AdhocMetricEditPopover extends 
PureComponent {
 }
 AdhocMetricEditPopover.propTypes = propTypes;
 AdhocMetricEditPopover.defaultProps = defaultProps;
+
+// Map Redux state to props to get access to datasource with disabled states
+const mapStateToProps = state => ({
+  reduxDatasource: state.explore?.datasource,
+});
+
+export default connect(mapStateToProps)(AdhocMetricEditPopover);
diff --git 
a/superset-frontend/src/explore/components/controls/SemanticLayerVerification.tsx
 
b/superset-frontend/src/explore/components/controls/SemanticLayerVerification.tsx
index d1698b4c75..fc58512bc7 100644
--- 
a/superset-frontend/src/explore/components/controls/SemanticLayerVerification.tsx
+++ 
b/superset-frontend/src/explore/components/controls/SemanticLayerVerification.tsx
@@ -63,23 +63,17 @@ export function collectQueryFields(formData: any): {
   }
   if (formData.series) {
     dimensions.push(
-      ...(Array.isArray(formData.series)
-        ? formData.series
-        : [formData.series]),
+      ...(Array.isArray(formData.series) ? formData.series : 
[formData.series]),
     );
   }
   if (formData.entity) {
     dimensions.push(
-      ...(Array.isArray(formData.entity)
-        ? formData.entity
-        : [formData.entity]),
+      ...(Array.isArray(formData.entity) ? formData.entity : 
[formData.entity]),
     );
   }
   if (formData.x_axis) {
     dimensions.push(
-      ...(Array.isArray(formData.x_axis)
-        ? formData.x_axis
-        : [formData.x_axis]),
+      ...(Array.isArray(formData.x_axis) ? formData.x_axis : 
[formData.x_axis]),
     );
   }
 
@@ -139,7 +133,7 @@ export function collectQueryFields(formData: any): {
     dimensions: [...new Set(cleanDimensions)], // Remove duplicates
     metrics: [...new Set(cleanMetrics)], // Remove duplicates
   };
-  
+
   console.log('collectQueryFields output:', result);
   return result;
 }
@@ -157,7 +151,10 @@ function supportsSemanticLayerVerification(datasource: 
Dataset): boolean {
 }
 
 // Cache for API calls to prevent duplicates
-const apiCallCache = new Map<string, Promise<{ dimensions: string[]; metrics: 
string[] } | null>>();
+const apiCallCache = new Map<
+  string,
+  Promise<{ dimensions: string[]; metrics: string[] } | null>
+>();
 
 /**
  * Call the validation API
@@ -192,7 +189,9 @@ export async function callValidationAPI(
         dimensions: selectedDimensions,
         metrics: selectedMetrics,
       },
-    }).then(response => response.json as { dimensions: string[]; metrics: 
string[] });
+    }).then(
+      response => response.json as { dimensions: string[]; metrics: string[] },
+    );
 
     // Cache the promise
     apiCallCache.set(cacheKey, apiPromise);
@@ -230,20 +229,24 @@ export function createMetricsVerification(controlName?: 
string): AsyncVerify {
 
     // Defer the actual verification to allow React/Redux to complete all 
updates
     // This should solve the stale state issue by waiting for the event loop 
to complete
-    return new Promise((resolve) => {
+    return new Promise(resolve => {
       setTimeout(async () => {
         console.log('=== METRICS VERIFICATION DEFERRED EXECUTION ===');
-        
+
         // Try to get fresh state from Redux store
-        const store = (window as any).__REDUX_STORE__ || (actions as 
any)?._store;
+        const store =
+          (window as any).__REDUX_STORE__ || (actions as any)?._store;
         let currentFormData = form_data;
-        
+
         if (store) {
           try {
             const state = store.getState();
             const exploreState = state.explore || {};
             currentFormData = exploreState.form_data || form_data;
-            console.log('Fresh form_data from store after delay:', 
currentFormData);
+            console.log(
+              'Fresh form_data from store after delay:',
+              currentFormData,
+            );
           } catch (error) {
             console.warn('Could not access Redux store:', error);
           }
@@ -291,20 +294,30 @@ export function createMetricsVerification(controlName?: 
string): AsyncVerify {
         let updatedDatasourceColumns = dataset.columns;
 
         // Filter valid names to only include those that exist in the original 
datasource
-        const originalDimensionNames = new Set(dataset.columns?.map((col: any) 
=> col.column_name) || []);
-        const originalMetricNames = new Set(dataset.metrics?.map((metric: any) 
=> metric.metric_name) || []);
-        
+        const originalDimensionNames = new Set(
+          dataset.columns?.map((col: any) => col.column_name) || [],
+        );
+        const originalMetricNames = new Set(
+          dataset.metrics?.map((metric: any) => metric.metric_name) || [],
+        );
+
         const filteredValidMetricNames = new Set(
-          validationResult.metrics.filter(metric => 
originalMetricNames.has(metric))
+          validationResult.metrics.filter(metric =>
+            originalMetricNames.has(metric),
+          ),
         );
         const filteredValidDimensionNames = new Set(
-          validationResult.dimensions.filter(dim => 
originalDimensionNames.has(dim))
+          validationResult.dimensions.filter(dim =>
+            originalDimensionNames.has(dim),
+          ),
         );
 
         if (dataset.metrics) {
           updatedDatasourceMetrics = dataset.metrics.map((metric: any) => ({
             ...metric,
-            isDisabled: !filteredValidMetricNames.has(metric.metric_name || 
metric),
+            isDisabled: !filteredValidMetricNames.has(
+              metric.metric_name || metric,
+            ),
           }));
         }
 
@@ -312,7 +325,9 @@ export function createMetricsVerification(controlName?: 
string): AsyncVerify {
         if (dataset.columns) {
           updatedDatasourceColumns = dataset.columns.map((column: any) => ({
             ...column,
-            isDisabled: !filteredValidDimensionNames.has(column.column_name || 
column),
+            isDisabled: !filteredValidDimensionNames.has(
+              column.column_name || column,
+            ),
           }));
         }
 
@@ -356,20 +371,24 @@ export function createColumnsVerification(controlName?: 
string): AsyncVerify {
 
     // Defer the actual verification to allow React/Redux to complete all 
updates
     // This should solve the stale state issue by waiting for the event loop 
to complete
-    return new Promise((resolve) => {
+    return new Promise(resolve => {
       setTimeout(async () => {
         console.log('=== COLUMNS VERIFICATION DEFERRED EXECUTION ===');
-        
+
         // Try to get fresh state from Redux store
-        const store = (window as any).__REDUX_STORE__ || (actions as 
any)?._store;
+        const store =
+          (window as any).__REDUX_STORE__ || (actions as any)?._store;
         let currentFormData = form_data;
-        
+
         if (store) {
           try {
             const state = store.getState();
             const exploreState = state.explore || {};
             currentFormData = exploreState.form_data || form_data;
-            console.log('Fresh form_data from store after delay:', 
currentFormData);
+            console.log(
+              'Fresh form_data from store after delay:',
+              currentFormData,
+            );
           } catch (error) {
             console.warn('Could not access Redux store:', error);
           }
@@ -418,28 +437,40 @@ export function createColumnsVerification(controlName?: 
string): AsyncVerify {
         let updatedDatasourceMetrics = dataset.metrics;
 
         // Filter valid names to only include those that exist in the original 
datasource
-        const originalDimensionNames = new Set(dataset.columns?.map((col: any) 
=> col.column_name) || []);
-        const originalMetricNames = new Set(dataset.metrics?.map((metric: any) 
=> metric.metric_name) || []);
-        
+        const originalDimensionNames = new Set(
+          dataset.columns?.map((col: any) => col.column_name) || [],
+        );
+        const originalMetricNames = new Set(
+          dataset.metrics?.map((metric: any) => metric.metric_name) || [],
+        );
+
         const filteredValidDimensionNames = new Set(
-          validationResult.dimensions.filter(dim => 
originalDimensionNames.has(dim))
+          validationResult.dimensions.filter(dim =>
+            originalDimensionNames.has(dim),
+          ),
         );
         const filteredValidMetricNames = new Set(
-          validationResult.metrics.filter(metric => 
originalMetricNames.has(metric))
+          validationResult.metrics.filter(metric =>
+            originalMetricNames.has(metric),
+          ),
         );
 
         // Update the disabled state logic to use filtered valid names
         if (dataset.columns) {
           updatedDatasourceColumns = dataset.columns.map((column: any) => ({
             ...column,
-            isDisabled: !filteredValidDimensionNames.has(column.column_name || 
column),
+            isDisabled: !filteredValidDimensionNames.has(
+              column.column_name || column,
+            ),
           }));
         }
 
         if (dataset.metrics) {
           updatedDatasourceMetrics = dataset.metrics.map((metric: any) => ({
             ...metric,
-            isDisabled: !filteredValidMetricNames.has(metric.metric_name || 
metric),
+            isDisabled: !filteredValidMetricNames.has(
+              metric.metric_name || metric,
+            ),
           }));
         }
 
@@ -502,7 +533,7 @@ export const SEMANTIC_LAYER_CONTROL_FIELDS = [
   'y',
   'size',
   'secondary_metric',
-  
+
   // Dimension controls
   'groupby',
   'columns',
diff --git 
a/superset-frontend/src/explore/components/controls/withAsyncVerification.tsx 
b/superset-frontend/src/explore/components/controls/withAsyncVerification.tsx
index 92daa2ec73..41c0f83a3c 100644
--- 
a/superset-frontend/src/explore/components/controls/withAsyncVerification.tsx
+++ 
b/superset-frontend/src/explore/components/controls/withAsyncVerification.tsx
@@ -151,7 +151,6 @@ export default function withAsyncVerification({
     const [isLoading, setIsLoading] = useState<boolean>(initialIsLoading);
     const { addWarningToast } = restProps.actions;
     const verificationTriggeredByChange = useRef(false);
-    
 
     // memoize `restProps`, so that verification only triggers when material
     // props are actually updated.
@@ -205,7 +204,7 @@ export default function withAsyncVerification({
         if (onChange) {
           onChange(value, { ...otherProps, ...verifiedProps });
         }
-        
+
         // Trigger verification with the new value if verification is enabled
         if (needAsyncVerification && verify) {
           verificationTriggeredByChange.current = true;
@@ -213,7 +212,14 @@ export default function withAsyncVerification({
           verifyProps(verify, propsWithNewValue);
         }
       },
-      [basicOnChange, otherProps, verifiedProps, needAsyncVerification, 
verify, verifyProps],
+      [
+        basicOnChange,
+        otherProps,
+        verifiedProps,
+        needAsyncVerification,
+        verify,
+        verifyProps,
+      ],
     );
 
     useEffect(() => {
@@ -225,7 +231,13 @@ export default function withAsyncVerification({
         }
         verifyProps(verify, otherProps);
       }
-    }, [needAsyncVerification, verify, otherProps, verifyProps, 
skipEffectVerification]);
+    }, [
+      needAsyncVerification,
+      verify,
+      otherProps,
+      verifyProps,
+      skipEffectVerification,
+    ]);
 
     return (
       <ControlComponent


Reply via email to