diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx index a8a4bba5c61..26162db2d15 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx @@ -19,7 +19,6 @@ /* eslint-disable react-hooks/rules-of-hooks */ import { ColumnMeta, Metric } from '@superset-ui/chart-controls'; import { - AdhocFilter, Behavior, ChartDataResponseResult, Column, 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 2a97d2426a3..00df5927216 100644 --- a/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx +++ b/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx @@ -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; +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( diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndMetricSelect.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndMetricSelect.tsx index f2a4faaddbc..efdca7d7446 100644 --- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndMetricSelect.tsx +++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndMetricSelect.tsx @@ -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), column }); + return new AdhocMetric({ ...(metric as unknown as Record), column: column as unknown as Record }); } } // 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 = { + // Cast config to handle ColumnMeta/ColumnType mismatch + const config = { column: itemValue, - }; + } as Partial; if (itemValue.type_generic === GenericDataType.Numeric) { config.aggregate = AGGREGATES.SUM; } else if ( 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 25fb667c273..2bae9c5a997 100644 --- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/index.ts +++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/index.ts @@ -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; 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 2efd563db60..284322a4cf1 100644 --- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterControl/index.tsx +++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterControl/index.tsx @@ -394,13 +394,13 @@ class AdhocFilterControl extends Component || {}} options={this.state.options} - onFilterEdit={this.onNewFilter} - partitionColumn={this.state.partitionColumn} + onFilterEdit={this.onNewFilter as (editedFilter: AdhocFilter) => void} + partitionColumn={this.state.partitionColumn ?? undefined} > {trigger} diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopover/AdhocFilterEditPopover.test.tsx b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopover/AdhocFilterEditPopover.test.tsx index 8419f8850a4..3f8eecf452b 100644 --- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopover/AdhocFilterEditPopover.test.tsx +++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopover/AdhocFilterEditPopover.test.tsx @@ -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; const renderPopover = (props: Partial = {}) => - render(, { - useRedux: true, // Add Redux provider for context - }); + render( + )} + />, + { + useRedux: true, // Add Redux provider for context + }, + ); // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks describe('AdhocFilterEditPopover', () => { diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopover/index.tsx b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopover/index.tsx index 546057a6614..c390a4a87d7 100644 --- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopover/index.tsx +++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopover/index.tsx @@ -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 (