From 6878882d9ff890a915f4155e17808a184c3fe4e2 Mon Sep 17 00:00:00 2001 From: Evan Rusackas Date: Fri, 19 Dec 2025 09:12:50 -0800 Subject: [PATCH] fix(types): fix TypeScript errors in controls and filters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Revert DatasourceControlProps to required props and add proper TestProps interface in tests - Fix AdhocFilterOptionProps interface to match actual callback signatures - Add optional chaining for onChange callbacks in AdhocFilterControl - Fix AdhocFilter class type casting for translateToSql compatibility - Add @ts-expect-error for propTypes/defaultProps assignments 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../DatasourceControl.test.tsx | 33 ++++++++++++++++++- .../controls/DatasourceControl/index.tsx | 10 +++--- .../FilterControl/AdhocFilter/index.ts | 28 ++++++++++++---- .../AdhocFilterControl/index.tsx | 20 ++++++----- .../FilterControl/AdhocFilterOption/index.tsx | 15 +++++---- 5 files changed, 77 insertions(+), 29 deletions(-) diff --git a/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx b/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx index 1528f7c4b84..51fa206b356 100644 --- a/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx +++ b/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx @@ -73,8 +73,39 @@ const mockDatasource: TestDatasource = { sql: 'SELECT * FROM mock_datasource_sql', }; +interface TestProps { + hovered: boolean; + type: string; + label: string; + default: null; + description: null; + value: string; + form_data: JsonObject; + datasource: TestDatasource; + validationErrors: string[]; + name: string; + actions: { + changeDatasource: jest.Mock; + setControlValue: jest.Mock; + }; + isEditable: boolean; + user: { + createdOn: string; + email: string; + firstName: string; + isActive: boolean; + lastName: string; + permissions: JsonObject; + roles: JsonObject; + userId: number; + username: string; + }; + onChange: jest.Mock; + onDatasourceSave: jest.Mock; +} + // Use type assertion for test props since the component is wrapped with withTheme -const createProps = (overrides: JsonObject = {}): Record => ({ +const createProps = (overrides: JsonObject = {}): TestProps => ({ hovered: false, type: 'DatasourceControl', label: 'Datasource', diff --git a/superset-frontend/src/explore/components/controls/DatasourceControl/index.tsx b/superset-frontend/src/explore/components/controls/DatasourceControl/index.tsx index 0f4f3e81ad3..ebcc0cf6c42 100644 --- a/superset-frontend/src/explore/components/controls/DatasourceControl/index.tsx +++ b/superset-frontend/src/explore/components/controls/DatasourceControl/index.tsx @@ -82,15 +82,15 @@ interface FormData { } interface DatasourceControlProps { - actions?: DatasourceControlActions; + actions: DatasourceControlActions; onChange?: () => void; value?: string | null; - datasource?: ExtendedDatasource; + datasource: ExtendedDatasource; form_data?: FormData; isEditable?: boolean; onDatasourceSave?: ((datasource: ExtendedDatasource) => void) | null; - theme?: SupersetTheme; - user?: User; + theme: SupersetTheme; + user: User; // ControlHeader-related props hovered?: boolean; type?: string; @@ -99,8 +99,6 @@ interface DatasourceControlProps { description?: string | null; validationErrors?: string[]; name?: string; - // Allow additional props - [key: string]: unknown; } interface DatasourceControlState { diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/index.ts b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/index.ts index 3fccd7f4ace..267c77b901c 100644 --- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/index.ts +++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/index.ts @@ -77,7 +77,8 @@ export default class AdhocFilter { this.sqlExpression = typeof adhocFilter.sqlExpression === 'string' ? adhocFilter.sqlExpression - : translateToSql(adhocFilter, { useSimple: true }); + // Cast to unknown first to handle type mismatch between AdhocFilterInput and AdhocFilter from @superset-ui/core + : translateToSql(adhocFilter as unknown as Parameters[0], { useSimple: true }); this.clause = adhocFilter.clause; if ( adhocFilter.operator && @@ -106,12 +107,24 @@ export default class AdhocFilter { } duplicateWith(nextFields: Partial): AdhocFilter { - return new AdhocFilter({ - ...this, - // all duplicated fields are not new (i.e. will not open popup automatically) - isNew: false, + // Spread class properties as plain object for constructor input + const currentFields: AdhocFilterInput = { + expressionType: this.expressionType, + subject: this.subject, + operator: this.operator, + operatorId: this.operatorId, + comparator: this.comparator, + clause: this.clause, + sqlExpression: this.sqlExpression, + isExtra: this.isExtra, + isNew: false, // all duplicated fields are not new + datasourceWarning: this.datasourceWarning, + deck_slices: this.deck_slices, + layerFilterScope: this.layerFilterScope, + filterOptionName: this.filterOptionName, ...nextFields, - }); + }; + return new AdhocFilter(currentFields); } equals(adhocFilter: AdhocFilter): boolean { @@ -163,6 +176,7 @@ export default class AdhocFilter { } translateToSql(): string { - return translateToSql(this); + // Cast to unknown first to handle type mismatch between class and @superset-ui/core AdhocFilter type + return translateToSql(this as unknown as Parameters[0]); } } diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterControl/index.tsx b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterControl/index.tsx index 3ee8bf15931..6008ad44f23 100644 --- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterControl/index.tsx +++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterControl/index.tsx @@ -209,7 +209,7 @@ class AdhocFilterControl extends Component this.props.onChange(this.state.values)} + onDropLabel={() => this.props.onChange?.(this.state.values)} partitionColumn={this.state.partitionColumn} /> ); @@ -282,7 +282,7 @@ class AdhocFilterControl extends Component { - this.props.onChange(this.state.values); + this.props.onChange?.(this.state.values); }, ); } } onFilterEdit(changedFilter: AdhocFilter): void { - this.props.onChange( + this.props.onChange?.( this.state.values.map(value => { if (value.filterOptionName === changedFilter.filterOptionName) { return changedFilter; @@ -325,14 +325,15 @@ class AdhocFilterControl extends Component this.mapOption(option)) - .filter(option => option); - this.props.onChange(options); + .filter((option): option is AdhocFilter => option !== null); + this.props.onChange?.(options); } getMetricExpression(savedMetricName: string): string { - return this.props.savedMetrics.find( + const metric = this.props.savedMetrics?.find( savedMetric => savedMetric.metric_name === savedMetricName, - ).expression; + ); + return metric?.expression ?? ''; } moveLabel(dragIndex: number, hoverIndex: number): void { @@ -429,7 +430,10 @@ class AdhocFilterControl extends Component void; - onRemoveFilter: () => void; + onFilterEdit: (editedFilter: AdhocFilter) => void; + onRemoveFilter: (e: React.MouseEvent) => void; options: OptionSortType[]; - sections: string[]; - operators: Operators[]; - datasource: Record; - partitionColumn: string; - onMoveLabel: () => void; + sections?: string[]; + operators?: string[]; + datasource?: Record; + partitionColumn?: string | null; + onMoveLabel: (dragIndex: number, hoverIndex: number) => void; onDropLabel: () => void; index: number; }