fix(types): resolve remaining TypeScript CI errors

- Remove unused AdhocFilter import from FiltersConfigForm.tsx
- Fix DndMetricSelect.tsx ColumnMeta type cast for AdhocMetric column
- Add Operators import and cast operatorId in AdhocFilter/index.ts
- Cast operators prop in AdhocFilterControl/index.tsx
- Fix deck_slices type access and add parameter types in AdhocFilterEditPopover
- Use duplicateWith for creating corrected AdhocFilter instance
- Add React import and cast props in DatasourceControl.test.tsx
- Add React import and cast props in AdhocFilterEditPopover.test.tsx

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Evan Rusackas
2025-12-19 12:22:07 -08:00
parent 586e03cb80
commit c6e1fe5f91
7 changed files with 82 additions and 67 deletions

View File

@@ -17,6 +17,7 @@
* under the License.
*/
import type React from 'react';
import { Route } from 'react-router-dom';
import fetchMock from 'fetch-mock';
import { DatasourceType, JsonObject, SupersetClient } from '@superset-ui/core';
@@ -107,37 +108,40 @@ interface TestProps {
}
// Use type assertion for test props since the component is wrapped with withTheme
const createProps = (overrides: JsonObject = {}): TestProps => ({
hovered: false,
type: 'DatasourceControl',
label: 'Datasource',
default: null,
description: null,
value: '25__table',
form_data: {},
datasource: mockDatasource,
validationErrors: [],
name: 'datasource',
actions: {
changeDatasource: jest.fn(),
setControlValue: jest.fn(),
},
isEditable: true,
user: {
createdOn: '2021-04-27T18:12:38.952304',
email: 'admin',
firstName: 'admin',
isActive: true,
lastName: 'admin',
permissions: {},
roles: { Admin: Array(173) },
userId: 1,
username: 'admin',
},
onChange: jest.fn(),
onDatasourceSave: jest.fn(),
...overrides,
});
// The withTheme HOC makes the props type complex, so we use a cast to bypass the type check
type DatasourceControlComponentProps = React.ComponentProps<typeof DatasourceControl>;
const createProps = (overrides: JsonObject = {}): DatasourceControlComponentProps =>
({
hovered: false,
type: 'DatasourceControl',
label: 'Datasource',
default: null,
description: null,
value: '25__table',
form_data: {},
datasource: mockDatasource,
validationErrors: [],
name: 'datasource',
actions: {
changeDatasource: jest.fn(),
setControlValue: jest.fn(),
},
isEditable: true,
user: {
createdOn: '2021-04-27T18:12:38.952304',
email: 'admin',
firstName: 'admin',
isActive: true,
lastName: 'admin',
permissions: {},
roles: { Admin: Array(173) },
userId: 1,
username: 'admin',
},
onChange: jest.fn(),
onDatasourceSave: jest.fn(),
...overrides,
}) as DatasourceControlComponentProps;
async function openAndSaveChanges(datasource: TestDatasource) {
fetchMock.get(

View File

@@ -90,7 +90,7 @@ const coerceMetrics = (
);
if (column) {
// Cast to unknown first to handle type mismatch between @superset-ui/core and local AdhocMetric
return new AdhocMetric({ ...(metric as unknown as Record<string, unknown>), column });
return new AdhocMetric({ ...(metric as unknown as Record<string, unknown>), column: column as unknown as Record<string, unknown> });
}
}
// Cast to unknown first to handle type mismatch between @superset-ui/core and local AdhocMetric
@@ -349,9 +349,10 @@ const DndMetricSelect = (props: any) => {
droppedItem.type === DndItemType.Column
) {
const itemValue = droppedItem.value as ColumnMeta;
const config: Partial<AdhocMetric> = {
// Cast config to handle ColumnMeta/ColumnType mismatch
const config = {
column: itemValue,
};
} as Partial<AdhocMetric>;
if (itemValue.type_generic === GenericDataType.Numeric) {
config.aggregate = AGGREGATES.SUM;
} else if (

View File

@@ -20,6 +20,7 @@ import {
CUSTOM_OPERATORS,
DISABLE_INPUT_OPERATORS,
OPERATOR_ENUM_TO_OPERATOR_TYPE,
Operators,
} from 'src/explore/constants';
import { translateToSql } from '../utils/translateToSQL';
import { Clauses, ExpressionTypes } from '../types';
@@ -68,7 +69,7 @@ export default class AdhocFilter {
this.operator = adhocFilter.operator?.toUpperCase();
this.operatorId = adhocFilter.operatorId;
this.comparator = adhocFilter.comparator;
if (adhocFilter.operatorId && DISABLE_INPUT_OPERATORS.indexOf(adhocFilter.operatorId) >= 0) {
if (adhocFilter.operatorId && DISABLE_INPUT_OPERATORS.indexOf(adhocFilter.operatorId as Operators) >= 0) {
this.comparator = undefined;
}
this.clause = adhocFilter.clause || Clauses.Where;

View File

@@ -394,13 +394,13 @@ class AdhocFilterControl extends Component<AdhocFilterControlProps, AdhocFilterC
addNewFilterPopoverTrigger(trigger: ReactNode): JSX.Element {
return (
<AdhocFilterPopoverTrigger
operators={this.props.operators}
operators={this.props.operators as Operators[] | undefined}
sections={this.props.sections}
adhocFilter={new AdhocFilter({})}
datasource={this.props.datasource}
datasource={this.props.datasource as Record<string, unknown> || {}}
options={this.state.options}
onFilterEdit={this.onNewFilter}
partitionColumn={this.state.partitionColumn}
onFilterEdit={this.onNewFilter as (editedFilter: AdhocFilter) => void}
partitionColumn={this.state.partitionColumn ?? undefined}
>
{trigger}
</AdhocFilterPopoverTrigger>

View File

@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import type React from 'react';
import { render, screen, fireEvent } from 'spec/helpers/testing-library';
import userEvent from '@testing-library/user-event';
import { AGGREGATES } from 'src/explore/constants';
@@ -70,10 +71,18 @@ const defaultProps = {
datasource: {},
};
// Cast props to handle AdhocMetric type in options array
type AdhocFilterEditPopoverComponentProps = React.ComponentProps<typeof AdhocFilterEditPopover>;
const renderPopover = (props: Partial<typeof defaultProps> = {}) =>
render(<AdhocFilterEditPopover {...defaultProps} {...props} />, {
useRedux: true, // Add Redux provider for context
});
render(
<AdhocFilterEditPopover
{...(defaultProps as unknown as AdhocFilterEditPopoverComponentProps)}
{...(props as unknown as Partial<AdhocFilterEditPopoverComponentProps>)}
/>,
{
useRedux: true, // Add Redux provider for context
},
);
// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks
describe('AdhocFilterEditPopover', () => {

View File

@@ -34,6 +34,7 @@ import columnType from 'src/explore/components/controls/FilterControl/columnType
import {
POPOVER_INITIAL_HEIGHT,
POPOVER_INITIAL_WIDTH,
Operators,
} from 'src/explore/constants';
import rison from 'rison';
import { isObject } from 'lodash';
@@ -210,9 +211,8 @@ export default class AdhocFilterEditPopover extends Component<
}
onSave() {
const hasDeckSlices =
this.state.adhocFilter.deck_slices &&
this.state.adhocFilter.deck_slices.length > 0;
const deckSlices = this.state.adhocFilter.deck_slices as number[] | undefined;
const hasDeckSlices = deckSlices && deckSlices.length > 0;
if (!hasDeckSlices) {
this.props.onChange(this.state.adhocFilter);
@@ -226,10 +226,9 @@ export default class AdhocFilterEditPopover extends Component<
}
return item;
});
const correctedAdhocFilter = {
...this.state.adhocFilter,
const correctedAdhocFilter = this.state.adhocFilter.duplicateWith({
layerFilterScope: selectedLayers,
};
});
this.setState({ hasLayerFilterScopeChanged: false });
this.props.onChange(correctedAdhocFilter);
this.props.onClose();
@@ -261,17 +260,17 @@ export default class AdhocFilterEditPopover extends Component<
document.removeEventListener('mousemove', this.onMouseMove);
}
onTabChange(activeKey) {
onTabChange(activeKey: string) {
this.setState({
activeKey,
});
}
adjustHeight(heightDifference) {
adjustHeight(heightDifference: number) {
this.setState(state => ({ height: state.height + heightDifference }));
}
loadLayerOptions(page, pageSize) {
loadLayerOptions(page: number, pageSize: number) {
const query = rison.encode({
columns: ['id', 'slice_name', 'viz_type'],
filters: [{ col: 'viz_type', opr: 'sw', value: 'deck' }],
@@ -297,7 +296,7 @@ export default class AdhocFilterEditPopover extends Component<
};
}
const deckSlices = this.props.adhocFilter?.deck_slices || [];
const deckSlices = (this.props.adhocFilter?.deck_slices || []) as number[];
const list = [
{
@@ -306,7 +305,7 @@ export default class AdhocFilterEditPopover extends Component<
label: 'All',
},
...response.json.result
.map(item => {
.map((item: { id: number; slice_name: string }) => {
const sliceIndex = deckSlices.indexOf(item.id);
return {
id: item.id,
@@ -315,8 +314,8 @@ export default class AdhocFilterEditPopover extends Component<
sliceIndex,
};
})
.filter(item => item.sliceIndex !== -1)
.map(({ sliceIndex, ...item }) => item),
.filter((item: { sliceIndex: number }) => item.sliceIndex !== -1)
.map(({ sliceIndex, ...item }: { sliceIndex: number; id: number; value: number; label: string }) => item),
];
return {
@@ -326,24 +325,26 @@ export default class AdhocFilterEditPopover extends Component<
});
}
onLayerChange(selectedValue) {
let updatedSelectedLayers = selectedValue;
onLayerChange(selectedValue: LayerOption[] | number[] | null) {
let updatedSelectedLayers: LayerOption[] = selectedValue as LayerOption[] || [];
if (!selectedValue || selectedValue.length === 0) {
updatedSelectedLayers = [{ id: null, value: -1, label: 'All' }];
} else if (
selectedValue.length > 1 &&
selectedValue.some(item => item.value === -1 || item === -1)
selectedValue.some((item: LayerOption | number) =>
(typeof item === 'object' && item.value === -1) || item === -1
)
) {
const lastItem = selectedValue[selectedValue.length - 1];
if (
selectedValue[selectedValue.length - 1].value === -1 ||
selectedValue[selectedValue.length - 1] === -1
(typeof lastItem === 'object' && lastItem.value === -1) ||
lastItem === -1
) {
updatedSelectedLayers = [{ id: null, value: -1, label: 'All' }];
} else {
updatedSelectedLayers = selectedValue
.filter(item => item.value !== -1)
.filter(item => item !== -1);
updatedSelectedLayers = (selectedValue as LayerOption[])
.filter((item: LayerOption) => item.value !== -1);
}
}
@@ -374,8 +375,8 @@ export default class AdhocFilterEditPopover extends Component<
!adhocFilter.equals(propsAdhocFilter) ||
hasLayerFilterScopeChanged;
const hasDeckSlices =
adhocFilter.deck_slices && adhocFilter.deck_slices.length > 0;
const renderDeckSlices = adhocFilter.deck_slices as number[] | undefined;
const hasDeckSlices = renderDeckSlices && renderDeckSlices.length > 0;
return (
<FilterPopoverContentContainer
@@ -399,7 +400,7 @@ export default class AdhocFilterEditPopover extends Component<
children: (
<ErrorBoundary>
<AdhocFilterEditPopoverSimpleTabContent
operators={operators}
operators={operators as Operators[] | undefined}
adhocFilter={this.state.adhocFilter}
onChange={this.onAdhocFilterChange}
options={options}