This is an automated email from the ASF dual-hosted git repository.
asoare pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 880cab58c33 fix(bug): Error when adding a filter using custom sql
(#38246)
880cab58c33 is described below
commit 880cab58c33c6320b3f7e387a41a9b1f0d04843e
Author: Alexandru Soare <[email protected]>
AuthorDate: Thu Mar 5 11:39:21 2026 +0200
fix(bug): Error when adding a filter using custom sql (#38246)
---
.../packages/superset-core/src/api/editors.ts | 12 ++++
.../src/core/editors/AceEditorProvider.tsx | 4 ++
.../AdhocFilterEditPopoverSqlTabContent.test.tsx | 84 ++++++++++++++++++----
.../AdhocFilterEditPopoverSqlTabContent/index.tsx | 8 +--
.../MetricControl/AdhocMetricEditPopover/index.tsx | 21 ++----
5 files changed, 99 insertions(+), 30 deletions(-)
diff --git a/superset-frontend/packages/superset-core/src/api/editors.ts
b/superset-frontend/packages/superset-core/src/api/editors.ts
index 1113bc8d729..cb151be75fa 100644
--- a/superset-frontend/packages/superset-core/src/api/editors.ts
+++ b/superset-frontend/packages/superset-core/src/api/editors.ts
@@ -480,6 +480,18 @@ export interface EditorHandle {
* @returns A Disposable that removes the provider when disposed
*/
registerCompletionProvider(provider: CompletionProvider): Disposable;
+
+ /**
+ * Force the editor to recalculate its dimensions.
+ * Called when the container size changes or when the editor becomes
+ * visible after being hidden (e.g., in a tab).
+ *
+ * Each editor implementation maps this to their equivalent:
+ * - Ace: editor.resize()
+ * - Monaco: editor.layout()
+ * - CodeMirror: editor.requestMeasure()
+ */
+ resize(): void;
}
/**
diff --git a/superset-frontend/src/core/editors/AceEditorProvider.tsx
b/superset-frontend/src/core/editors/AceEditorProvider.tsx
index 9236403a3b3..abdf76d14f1 100644
--- a/superset-frontend/src/core/editors/AceEditorProvider.tsx
+++ b/superset-frontend/src/core/editors/AceEditorProvider.tsx
@@ -182,6 +182,10 @@ const createAceEditorHandle = (
completionProviders.current.delete(provider.id);
});
},
+
+ resize: () => {
+ aceEditorRef.current?.editor?.resize();
+ },
});
/**
diff --git
a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSqlTabContent/AdhocFilterEditPopoverSqlTabContent.test.tsx
b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSqlTabContent/AdhocFilterEditPopoverSqlTabContent.test.tsx
index 2a29116e118..f5b1eee033f 100644
---
a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSqlTabContent/AdhocFilterEditPopoverSqlTabContent.test.tsx
+++
b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSqlTabContent/AdhocFilterEditPopoverSqlTabContent.test.tsx
@@ -22,30 +22,52 @@ import {
screen,
selectOption,
userEvent,
+ waitFor,
} from 'spec/helpers/testing-library';
import AdhocFilter from '../AdhocFilter';
import { Clauses, ExpressionTypes } from '../types';
import AdhocFilterEditPopoverSqlTabContent from '.';
+// Track resize calls for testing
+const mockResize = jest.fn();
+
+// Mock EditorHost with ref support for resize
+jest.mock('src/core/editors', () => {
+ const React = require('react');
+ return {
+ EditorHost: React.forwardRef(
+ (
+ {
+ value,
+ onChange,
+ }: {
+ value: string;
+ onChange: (v: string) => void;
+ },
+ ref: React.Ref<{ resize: () => void }>,
+ ) => {
+ React.useImperativeHandle(ref, () => ({
+ resize: mockResize,
+ }));
+ return (
+ <textarea
+ defaultValue={value}
+ onChange={e => onChange?.(e.target.value)}
+ />
+ );
+ },
+ ),
+ };
+});
+
// Add cleanup after each test
afterEach(async () => {
cleanup();
+ mockResize.mockClear();
// Wait for any pending effects to complete
await new Promise(resolve => setTimeout(resolve, 0));
});
-jest.mock('src/core/editors', () => ({
- EditorHost: ({
- value,
- onChange,
- }: {
- value: string;
- onChange: (v: string) => void;
- }) => (
- <textarea defaultValue={value} onChange={e => onChange?.(e.target.value)}
/>
- ),
-}));
-
const adhocFilter = new AdhocFilter({
expressionType: ExpressionTypes.Sql,
sqlExpression: 'value > 10',
@@ -89,3 +111,41 @@ test('calls onChange when the SQL expression changes',
async () => {
expect.objectContaining({ sqlExpression: input }),
);
});
+
+test('calls editor resize when adhocFilter changes', async () => {
+ const onChange = jest.fn();
+ const { rerender } = render(
+ <AdhocFilterEditPopoverSqlTabContent
+ adhocFilter={adhocFilter}
+ onChange={onChange}
+ options={[]}
+ height={100}
+ />,
+ );
+
+ // Initial render should call resize
+ await waitFor(() => {
+ expect(mockResize).toHaveBeenCalled();
+ });
+ mockResize.mockClear();
+
+ // Create a new filter to trigger the useEffect
+ const newFilter = new AdhocFilter({
+ expressionType: ExpressionTypes.Sql,
+ sqlExpression: 'value > 20',
+ clause: Clauses.Where,
+ });
+
+ rerender(
+ <AdhocFilterEditPopoverSqlTabContent
+ adhocFilter={newFilter}
+ onChange={onChange}
+ options={[]}
+ height={100}
+ />,
+ );
+
+ await waitFor(() => {
+ expect(mockResize).toHaveBeenCalled();
+ });
+});
diff --git
a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSqlTabContent/index.tsx
b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSqlTabContent/index.tsx
index 9ba11c617e0..00032523033 100644
---
a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSqlTabContent/index.tsx
+++
b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSqlTabContent/index.tsx
@@ -17,6 +17,7 @@
* under the License.
*/
import { useEffect, useRef, useMemo } from 'react';
+import type { editors } from '@apache-superset/core';
import { Select } from '@superset-ui/core/components';
import { t } from '@apache-superset/core';
import { css, styled, useTheme } from '@apache-superset/core/ui';
@@ -49,12 +50,11 @@ export default function
AdhocFilterEditPopoverSqlTabContent({
height: number;
datasource?: any;
}) {
- const aceEditorRef = useRef(null);
+ const editorRef = useRef<editors.EditorHandle>(null);
const theme = useTheme();
useEffect(() => {
- // @ts-expect-error - AceEditor ref type doesn't expose editor.resize()
- aceEditorRef?.current?.editor.resize();
+ editorRef.current?.resize();
}, [adhocFilter]);
const onSqlExpressionClauseChange = (clause: string) => {
@@ -125,7 +125,7 @@ export default function
AdhocFilterEditPopoverSqlTabContent({
`}
>
<SQLEditorWithValidation
- ref={aceEditorRef}
+ ref={editorRef}
keywords={keywords}
height={`${height - 130}px`}
onChange={onSqlExpressionChange}
diff --git
a/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.tsx
b/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.tsx
index 0f435dfa5b7..28f50d73dea 100644
---
a/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.tsx
+++
b/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.tsx
@@ -130,7 +130,7 @@ export default class AdhocMetricEditPopover extends
PureComponent<
// "Saved" is a default tab unless there are no saved metrics for dataset
defaultActiveTabKey = this.getDefaultTab();
- aceEditorRef: RefObject<editors.EditorHandle>;
+ editorRef: RefObject<editors.EditorHandle>;
dragStartX = 0;
@@ -152,8 +152,8 @@ export default class AdhocMetricEditPopover extends
PureComponent<
this.onMouseMove = this.onMouseMove.bind(this);
this.onMouseUp = this.onMouseUp.bind(this);
this.onTabChange = this.onTabChange.bind(this);
- this.aceEditorRef = createRef();
- this.refreshAceEditor = this.refreshAceEditor.bind(this);
+ this.editorRef = createRef();
+ this.refreshEditor = this.refreshEditor.bind(this);
this.getDefaultTab = this.getDefaultTab.bind(this);
this.state = {
@@ -313,20 +313,13 @@ export default class AdhocMetricEditPopover extends
PureComponent<
}
onTabChange(tab: string): void {
- this.refreshAceEditor();
+ this.refreshEditor();
this.props.getCurrentTab?.(tab);
}
- refreshAceEditor(): void {
+ refreshEditor(): void {
setTimeout(() => {
- if (this.aceEditorRef.current) {
- // Cast to access ace editor API
- (
- this.aceEditorRef.current as unknown as {
- editor?: { resize?: () => void };
- }
- ).editor?.resize?.();
- }
+ this.editorRef.current?.resize();
}, 0);
}
@@ -549,7 +542,7 @@ export default class AdhocMetricEditPopover extends
PureComponent<
children: (
<SQLEditorWithValidation
data-test="sql-editor"
- ref={this.aceEditorRef}
+ ref={this.editorRef}
keywords={keywords}
height={`${this.state.height - 120}px`}
onChange={this.onSqlExpressionChange}