korbit-ai[bot] commented on code in PR #34276:
URL: https://github.com/apache/superset/pull/34276#discussion_r2231527898


##########
superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/Shared_DeckGL.jsx:
##########
@@ -400,3 +400,83 @@ export const geojsonColumn = {
     }),
   },
 };
+
+const extractMetricsFromFormData = formData => {
+  const metrics = [];
+  // Extract from controls
+  Object.values(formData).forEach(value => {
+    if (value?.type === 'metric' && value?.value) {
+      metrics.push(value.value);
+    }
+  });
+  // Extract from metrics field
+  if (formData.metrics) {
+    metrics.push(
+      ...(Array.isArray(formData.metrics)
+        ? formData.metrics
+        : [formData.metrics]),
+    );
+  }
+  // Extract from point radius
+  if (formData.point_radius_fixed?.value) {
+    metrics.push(formData.point_radius_fixed.value);
+  }
+  return [...new Set(metrics)];

Review Comment:
   ### Missing Metric Value Validation <sub>![category 
Functionality](https://img.shields.io/badge/Functionality-0284c7)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The extractMetricsFromFormData function doesn't validate if metrics array 
contains null or undefined values before returning.
   
   
   ###### Why this matters
   If any metric value is null or undefined, it could cause issues when using 
these metrics in the tooltip template rendering.
   
   ###### Suggested change ∙ *Feature Preview*
   Add validation to filter out null or undefined metrics:
   ```javascript
   return [...new Set(metrics)].filter(metric => metric != null);
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/cb54e1f1-0db9-4dec-a08c-056b0efb5ae3/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/cb54e1f1-0db9-4dec-a08c-056b0efb5ae3?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/cb54e1f1-0db9-4dec-a08c-056b0efb5ae3?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/cb54e1f1-0db9-4dec-a08c-056b0efb5ae3?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/cb54e1f1-0db9-4dec-a08c-056b0efb5ae3)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:5f734e71-6568-463a-b973-97a20cdca8d5 -->
   
   
   [](5f734e71-6568-463a-b973-97a20cdca8d5)



##########
superset-frontend/src/explore/components/controls/TextAreaControl.jsx:
##########
@@ -73,16 +79,28 @@ const defaultProps = {
   textAreaStyles: {},
   tooltipOptions: {},
   hotkeys: [],
+  debounceDelay: null,
 };
 
 class TextAreaControl extends Component {
+  constructor(props) {
+    super(props);
+    if (props.debounceDelay) {
+      this.debouncedOnChange = debounce(props.onChange, props.debounceDelay);
+    }
+  }
+
   onControlChange(event) {
     const { value } = event.target;
     this.props.onChange(value);
   }
 
   onAreaEditorChange(value) {
-    this.props.onChange(value);
+    if (this.debouncedOnChange) {
+      this.debouncedOnChange(value);
+    } else {
+      this.props.onChange(value);
+    }

Review Comment:
   ### Missing Debounce Cleanup <sub>![category 
Performance](https://img.shields.io/badge/Performance-4f46e5)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The debounced function is created in the constructor but never cleaned up, 
which could lead to memory leaks and stale callbacks being executed after 
component unmounting.
   
   
   ###### Why this matters
   Without proper cleanup, the debounced function might continue to exist in 
memory and execute even after the component is unmounted, potentially causing 
memory leaks or calling callbacks on unmounted components.
   
   ###### Suggested change ∙ *Feature Preview*
   Add componentWillUnmount lifecycle method to cancel debounced callbacks:
   ```jsx
   componentWillUnmount() {
     if (this.debouncedOnChange) {
       this.debouncedOnChange.cancel();
     }
   }
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/d77cbb95-6099-40a2-adbd-f26562f9696f/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/d77cbb95-6099-40a2-adbd-f26562f9696f?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/d77cbb95-6099-40a2-adbd-f26562f9696f?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/d77cbb95-6099-40a2-adbd-f26562f9696f?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/d77cbb95-6099-40a2-adbd-f26562f9696f)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:2e7bbfca-0910-47b5-8649-427bd23a8c0d -->
   
   
   [](2e7bbfca-0910-47b5-8649-427bd23a8c0d)



##########
superset-frontend/src/explore/components/controls/DndColumnSelectControl/index.ts:
##########
@@ -16,7 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+/**
+ * Export DnD (Drag and Drop) control components:
+ * - DndSelectLabel: Base label component for DnD controls
+ * - DndColumnSelect: Column selection control
+ * - DndFilterSelect: Filter selection control
+ * - DndMetricSelect: Metric selection control
+ * - DndColumnMetricSelect: Combined column and metric selection control
+ */

Review Comment:
   ### Missing Purpose in Component Documentation <sub>![category 
Documentation](https://img.shields.io/badge/Documentation-7c3aed)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The documentation only lists what each component is, without explaining the 
purpose or use cases of these DnD controls in the context of the application.
   
   
   ###### Why this matters
   Without understanding the purpose of these controls, developers may misuse 
them or have difficulty choosing the appropriate component for their use case.
   
   ###### Suggested change ∙ *Feature Preview*
   /**
    * Export DnD (Drag and Drop) control components for building interactive 
chart controls.
    * These components enable users to configure visualizations by dragging and 
dropping
    * available columns, metrics, and filters.
    *
    * - DndSelectLabel: Base label component for DnD controls
    * - DndColumnSelect: Column selection control for choosing dataset columns
    * - DndFilterSelect: Filter selection control for data filtering
    * - DndMetricSelect: Metric selection control for aggregations
    * - DndColumnMetricSelect: Combined column and metric selection control
    */
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/196e42e7-1490-4e71-b854-96dc0e8bf1e2/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/196e42e7-1490-4e71-b854-96dc0e8bf1e2?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/196e42e7-1490-4e71-b854-96dc0e8bf1e2?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/196e42e7-1490-4e71-b854-96dc0e8bf1e2?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/196e42e7-1490-4e71-b854-96dc0e8bf1e2)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:ce246938-a6ae-4ec0-bb2b-af271f31c1dc -->
   
   
   [](ce246938-a6ae-4ec0-bb2b-af271f31c1dc)



##########
superset-frontend/src/explore/components/controls/TextAreaControl.jsx:
##########
@@ -27,6 +28,9 @@ import {
 } from '@superset-ui/core/components';
 import { t, withTheme } from '@superset-ui/core';
 
+// Import Ace Editor modes
+import 'ace-builds/src-min-noconflict/mode-handlebars';

Review Comment:
   ### Redundant Import Comment <sub>![category 
Documentation](https://img.shields.io/badge/Documentation-7c3aed)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The comment 'Import Ace Editor modes' is redundant as it simply states what 
the code does without providing any valuable context.
   
   
   ###### Why this matters
   Self-explanatory comments that just restate the code reduce signal-to-noise 
ratio and make the codebase harder to maintain.
   
   ###### Suggested change ∙ *Feature Preview*
   Remove the comment line `// Import Ace Editor modes` as the import statement 
is self-documenting.
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/a078e9c6-f8e3-4cfc-acdb-4eb894f96d2d/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/a078e9c6-f8e3-4cfc-acdb-4eb894f96d2d?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/a078e9c6-f8e3-4cfc-acdb-4eb894f96d2d?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/a078e9c6-f8e3-4cfc-acdb-4eb894f96d2d?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/a078e9c6-f8e3-4cfc-acdb-4eb894f96d2d)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:1b54b1b1-8864-4567-9931-24b5bcae2c74 -->
   
   
   [](1b54b1b1-8864-4567-9931-24b5bcae2c74)



##########
superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/multiValueUtils.ts:
##########
@@ -0,0 +1,150 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { QueryFormData } from '@superset-ui/core';
+
+/**
+ * Deck.gl chart types that perform aggregation and can have multiple values 
in tooltips
+ */
+export const AGGREGATED_DECK_GL_CHART_TYPES = [
+  'deck_screengrid',
+  'deck_heatmap',
+  'deck_contour',
+  'deck_hex',
+  'deck_grid',
+];
+
+/**
+ * Chart types that do NOT aggregate data (each tooltip shows single data 
point)
+ */
+export const NON_AGGREGATED_DECK_GL_CHART_TYPES = [
+  'deck_scatter',
+  'deck_arc',
+  'deck_path',
+  'deck_polygon',
+  'deck_geojson',
+];
+
+/**
+ * Determines if a deck.gl chart type aggregates data points
+ */
+export function isAggregatedDeckGLChart(vizType: string): boolean {
+  return AGGREGATED_DECK_GL_CHART_TYPES.includes(vizType);
+}
+
+/**
+ * Determines if a tooltip content field might contain multiple values
+ * for the given chart configuration
+ */
+export function fieldHasMultipleValues(
+  item: any,
+  formData: QueryFormData,
+): boolean {

Review Comment:
   ### Untyped parameter reduces type safety <sub>![category 
Design](https://img.shields.io/badge/Design-0d9488)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The function accepts an untyped 'any' parameter which violates type safety 
and makes the code harder to maintain.
   
   
   ###### Why this matters
   Using 'any' bypasses TypeScript's type checking system, making the code more 
prone to runtime errors and harder to refactor.
   
   ###### Suggested change ∙ *Feature Preview*
   ```typescript
   interface TooltipItem {
     item_type?: 'metric' | 'column';
     column_name?: string;
     metric_name?: string;
     label?: string;
   }
   
   export function fieldHasMultipleValues(
     item: TooltipItem | string,
     formData: QueryFormData,
   ): boolean {
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/cdc777bf-8030-4379-838d-0123a93f85d8/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/cdc777bf-8030-4379-838d-0123a93f85d8?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/cdc777bf-8030-4379-838d-0123a93f85d8?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/cdc777bf-8030-4379-838d-0123a93f85d8?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/cdc777bf-8030-4379-838d-0123a93f85d8)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:985df841-6cf5-4971-ac13-cd0c7dfb671a -->
   
   
   [](985df841-6cf5-4971-ac13-cd0c7dfb671a)



##########
superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/multiValueUtils.ts:
##########
@@ -0,0 +1,150 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { QueryFormData } from '@superset-ui/core';
+
+/**
+ * Deck.gl chart types that perform aggregation and can have multiple values 
in tooltips
+ */
+export const AGGREGATED_DECK_GL_CHART_TYPES = [
+  'deck_screengrid',
+  'deck_heatmap',
+  'deck_contour',
+  'deck_hex',
+  'deck_grid',
+];
+
+/**
+ * Chart types that do NOT aggregate data (each tooltip shows single data 
point)
+ */
+export const NON_AGGREGATED_DECK_GL_CHART_TYPES = [
+  'deck_scatter',
+  'deck_arc',
+  'deck_path',
+  'deck_polygon',
+  'deck_geojson',
+];
+
+/**
+ * Determines if a deck.gl chart type aggregates data points
+ */
+export function isAggregatedDeckGLChart(vizType: string): boolean {
+  return AGGREGATED_DECK_GL_CHART_TYPES.includes(vizType);
+}
+
+/**
+ * Determines if a tooltip content field might contain multiple values
+ * for the given chart configuration
+ */
+export function fieldHasMultipleValues(
+  item: any,
+  formData: QueryFormData,
+): boolean {
+  // Only aggregated deck.gl charts can have multiple values
+  if (!isAggregatedDeckGLChart(formData.viz_type)) {
+    return false;
+  }
+
+  // Skip metrics for now - they are typically aggregated already
+  if (item?.item_type === 'metric') {
+    return false;
+  }
+
+  // Only screengrid reliably supports multi-value fields with individual 
point access
+  // Other aggregated charts (hex, grid, heatmap, contour) use different 
aggregation methods
+  const supportsMultiValue = ['deck_screengrid'].includes(formData.viz_type);
+
+  if (!supportsMultiValue) {
+    return false;
+  }
+
+  // Columns in aggregated charts can have multiple values
+  if (item?.item_type === 'column') {
+    return true;
+  }
+
+  // String columns can have multiple values
+  if (typeof item === 'string') {
+    return true;
+  }
+
+  return false;
+}
+
+/**
+ * Creates a default template that limits multi-value fields
+ */
+export function createDefaultTemplateWithLimits(
+  tooltipContents: any[],
+  formData: QueryFormData,
+): string {

Review Comment:
   ### Function violates SRP with embedded helpers <sub>![category 
Design](https://img.shields.io/badge/Design-0d9488)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   Function accepts an array of 'any' type and contains helper functions that 
could be extracted to improve maintainability.
   
   
   ###### Why this matters
   The function violates the Single Responsibility Principle by containing 
helper functions and handling multiple concerns within a single function scope.
   
   ###### Suggested change ∙ *Feature Preview*
   ```typescript
   // Extract helper functions to module level
   const getFieldName = (item: TooltipItem | string): string | null => {
     // ... implementation
   };
   
   const getFieldLabel = (item: TooltipItem | string): string => {
     // ... implementation
   };
   
   export function createDefaultTemplateWithLimits(
     tooltipContents: TooltipItem[],
     formData: QueryFormData,
   ): string {
     // Main function logic without embedded helpers
   }
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/fd1679d2-73c7-4037-91b5-e350141087cd/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/fd1679d2-73c7-4037-91b5-e350141087cd?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/fd1679d2-73c7-4037-91b5-e350141087cd?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/fd1679d2-73c7-4037-91b5-e350141087cd?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/fd1679d2-73c7-4037-91b5-e350141087cd)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:fb3f4113-ff33-4e1d-9641-a6f4da0b41d4 -->
   
   
   [](fb3f4113-ff33-4e1d-9641-a6f4da0b41d4)



##########
superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/Shared_DeckGL.jsx:
##########
@@ -400,3 +400,83 @@ export const geojsonColumn = {
     }),
   },
 };
+
+const extractMetricsFromFormData = formData => {
+  const metrics = [];
+  // Extract from controls
+  Object.values(formData).forEach(value => {
+    if (value?.type === 'metric' && value?.value) {
+      metrics.push(value.value);
+    }
+  });

Review Comment:
   ### Inefficient Form Data Iteration <sub>![category 
Performance](https://img.shields.io/badge/Performance-4f46e5)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   Using Object.values() followed by forEach to iterate through all form data 
properties is inefficient, especially since we're only interested in specific 
properties.
   
   
   ###### Why this matters
   This approach unnecessarily iterates through all form data properties, 
including those that cannot possibly be metrics, leading to wasted CPU cycles 
on large form datasets.
   
   ###### Suggested change ∙ *Feature Preview*
   Replace with direct property access for known metric locations or use 
Object.entries() with early continue statements:
   ```javascript
   const extractMetricsFromFormData = formData => {
     const metrics = new Set();
     
     // Direct access to known metric locations
     if (formData.metrics) {
       (Array.isArray(formData.metrics) ? formData.metrics : [formData.metrics])
         .forEach(metric => metrics.add(metric));
     }
     
     if (formData.point_radius_fixed?.value) {
       metrics.add(formData.point_radius_fixed.value);
     }
     
     // For dynamic controls, only check relevant properties
     for (const [key, value] of Object.entries(formData)) {
       if (!value || typeof value !== 'object') continue;
       if (value.type === 'metric' && value.value) {
         metrics.add(value.value);
       }
     }
     
     return Array.from(metrics);
   }
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/24fc8506-e62b-46fc-98a2-28c78d9dcc83/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/24fc8506-e62b-46fc-98a2-28c78d9dcc83?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/24fc8506-e62b-46fc-98a2-28c78d9dcc83?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/24fc8506-e62b-46fc-98a2-28c78d9dcc83?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/24fc8506-e62b-46fc-98a2-28c78d9dcc83)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:9e81170e-9944-431a-9f41-57e1a6637d44 -->
   
   
   [](9e81170e-9944-431a-9f41-57e1a6637d44)



##########
superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/multiValueUtils.ts:
##########
@@ -0,0 +1,150 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { QueryFormData } from '@superset-ui/core';
+
+/**
+ * Deck.gl chart types that perform aggregation and can have multiple values 
in tooltips
+ */
+export const AGGREGATED_DECK_GL_CHART_TYPES = [
+  'deck_screengrid',
+  'deck_heatmap',
+  'deck_contour',
+  'deck_hex',
+  'deck_grid',
+];
+
+/**
+ * Chart types that do NOT aggregate data (each tooltip shows single data 
point)
+ */
+export const NON_AGGREGATED_DECK_GL_CHART_TYPES = [
+  'deck_scatter',
+  'deck_arc',
+  'deck_path',
+  'deck_polygon',
+  'deck_geojson',
+];
+
+/**
+ * Determines if a deck.gl chart type aggregates data points
+ */
+export function isAggregatedDeckGLChart(vizType: string): boolean {
+  return AGGREGATED_DECK_GL_CHART_TYPES.includes(vizType);
+}
+
+/**
+ * Determines if a tooltip content field might contain multiple values
+ * for the given chart configuration
+ */
+export function fieldHasMultipleValues(
+  item: any,
+  formData: QueryFormData,
+): boolean {
+  // Only aggregated deck.gl charts can have multiple values
+  if (!isAggregatedDeckGLChart(formData.viz_type)) {
+    return false;
+  }
+
+  // Skip metrics for now - they are typically aggregated already
+  if (item?.item_type === 'metric') {
+    return false;
+  }
+
+  // Only screengrid reliably supports multi-value fields with individual 
point access
+  // Other aggregated charts (hex, grid, heatmap, contour) use different 
aggregation methods
+  const supportsMultiValue = ['deck_screengrid'].includes(formData.viz_type);

Review Comment:
   ### Inconsistent Multi-Value Support <sub>![category 
Functionality](https://img.shields.io/badge/Functionality-0284c7)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The code claims support for multi-value fields only for 'deck_screengrid' 
while defining five aggregated chart types, creating an inconsistency between 
the defined capability and actual implementation.
   
   
   ###### Why this matters
   Users will expect multi-value support for all aggregated chart types but 
will find it only works for screengrid, leading to confusion and potential data 
visualization issues.
   
   ###### Suggested change ∙ *Feature Preview*
   Either update the AGGREGATED_DECK_GL_CHART_TYPES to only include 
'deck_screengrid' or implement multi-value support for all aggregated chart 
types. If temporary, add a TODO comment explaining the limitation:
   ```typescript
   // TODO: Currently only screengrid supports multi-value fields. Support for 
other aggregated charts will be added in future releases
   const supportsMultiValue = ['deck_screengrid'].includes(formData.viz_type);
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/e1ba9dfa-e12c-4523-9a1e-84be29af95ea/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/e1ba9dfa-e12c-4523-9a1e-84be29af95ea?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/e1ba9dfa-e12c-4523-9a1e-84be29af95ea?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/e1ba9dfa-e12c-4523-9a1e-84be29af95ea?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/e1ba9dfa-e12c-4523-9a1e-84be29af95ea)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:eaf79b11-d45e-44c3-a94e-ebde82182b0d -->
   
   
   [](eaf79b11-d45e-44c3-a94e-ebde82182b0d)



##########
superset-frontend/src/explore/components/controls/TextAreaControl.jsx:
##########
@@ -73,16 +79,28 @@ const defaultProps = {
   textAreaStyles: {},
   tooltipOptions: {},
   hotkeys: [],
+  debounceDelay: null,
 };
 
 class TextAreaControl extends Component {
+  constructor(props) {
+    super(props);
+    if (props.debounceDelay) {
+      this.debouncedOnChange = debounce(props.onChange, props.debounceDelay);
+    }
+  }
+
   onControlChange(event) {
     const { value } = event.target;
     this.props.onChange(value);
   }
 
   onAreaEditorChange(value) {
-    this.props.onChange(value);
+    if (this.debouncedOnChange) {
+      this.debouncedOnChange(value);
+    } else {
+      this.props.onChange(value);
+    }
   }

Review Comment:
   ### Duplicate Change Handler Logic <sub>![category 
Design](https://img.shields.io/badge/Design-0d9488)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The component has two similar methods handling changes (onControlChange and 
onAreaEditorChange) with different debouncing logic, violating the DRY 
principle.
   
   
   ###### Why this matters
   Having separate change handlers increases maintenance burden and creates 
inconsistent behavior between the text area and editor modes.
   
   ###### Suggested change ∙ *Feature Preview*
   Unify the change handling logic into a single method:
   ```javascript
   handleChange(value) {
       const finalValue = typeof value === 'object' ? value.target.value : 
value;
       if (this.debouncedOnChange) {
           this.debouncedOnChange(finalValue);
       } else {
           this.props.onChange(finalValue);
       }
   }
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/0fd7edfe-d9d1-4c4e-ab66-eaec236fdaa5/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/0fd7edfe-d9d1-4c4e-ab66-eaec236fdaa5?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/0fd7edfe-d9d1-4c4e-ab66-eaec236fdaa5?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/0fd7edfe-d9d1-4c4e-ab66-eaec236fdaa5?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/0fd7edfe-d9d1-4c4e-ab66-eaec236fdaa5)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:f4a2379e-3038-4f7a-b727-d9f88a1df050 -->
   
   
   [](f4a2379e-3038-4f7a-b727-d9f88a1df050)



##########
superset-frontend/src/explore/components/controls/TextAreaControl.jsx:
##########
@@ -73,16 +79,28 @@ const defaultProps = {
   textAreaStyles: {},
   tooltipOptions: {},
   hotkeys: [],
+  debounceDelay: null,
 };
 
 class TextAreaControl extends Component {
+  constructor(props) {
+    super(props);
+    if (props.debounceDelay) {
+      this.debouncedOnChange = debounce(props.onChange, props.debounceDelay);

Review Comment:
   ### Stale Callback in Debounced Function <sub>![category 
Functionality](https://img.shields.io/badge/Functionality-0284c7)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The debounced function is recreated only once in the constructor, which 
means it won't update if the onChange prop changes during the component 
lifecycle.
   
   
   ###### Why this matters
   If the parent component provides a new onChange callback, the debounced 
function will continue using the old callback, leading to incorrect behavior.
   
   ###### Suggested change ∙ *Feature Preview*
   Move the debounce creation to componentDidUpdate and update when props 
change:
   ```jsx
   componentDidUpdate(prevProps) {
     if (this.props.onChange !== prevProps.onChange && 
this.props.debounceDelay) {
       if (this.debouncedOnChange) {
         this.debouncedOnChange.cancel();
       }
       this.debouncedOnChange = debounce(this.props.onChange, 
this.props.debounceDelay);
     }
   }
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/ead54939-0358-4f3f-9a81-8a84a602920d/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/ead54939-0358-4f3f-9a81-8a84a602920d?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/ead54939-0358-4f3f-9a81-8a84a602920d?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/ead54939-0358-4f3f-9a81-8a84a602920d?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/ead54939-0358-4f3f-9a81-8a84a602920d)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:e113b19d-e9f2-45f0-8b94-0912080c2392 -->
   
   
   [](e113b19d-e9f2-45f0-8b94-0912080c2392)



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to