diff --git a/superset-frontend/packages/superset-ui-core/src/components/Button/types.ts b/superset-frontend/packages/superset-ui-core/src/components/Button/types.ts index 92e29eaeed3..d53e90e64e0 100644 --- a/superset-frontend/packages/superset-ui-core/src/components/Button/types.ts +++ b/superset-frontend/packages/superset-ui-core/src/components/Button/types.ts @@ -24,7 +24,6 @@ import type { ButtonVariantType, ButtonColorType, } from 'antd/es/button'; -import { IconType } from '@superset-ui/core/components/Icons/types'; import type { TooltipPlacement } from '../Tooltip/types'; export type { AntdButtonProps, ButtonType, ButtonVariantType, ButtonColorType }; @@ -49,5 +48,5 @@ export type ButtonProps = Omit & { buttonStyle?: ButtonStyle; cta?: boolean; showMarginRight?: boolean; - icon?: IconType; + icon?: ReactNode; }; diff --git a/superset-frontend/packages/superset-ui-core/src/components/Label/Label.test.tsx b/superset-frontend/packages/superset-ui-core/src/components/Label/Label.test.tsx index 216db7daa57..d57d1cffaaa 100644 --- a/superset-frontend/packages/superset-ui-core/src/components/Label/Label.test.tsx +++ b/superset-frontend/packages/superset-ui-core/src/components/Label/Label.test.tsx @@ -41,7 +41,6 @@ test('renders with monospace prop', () => { // test stories from the storybook! test('renders all the storybook gallery variants', () => { - // @ts-expect-error: Suppress TypeScript error for LabelGallery usage const { container } = render(); const nonInteractiveLabelCount = 4; const renderedLabelCount = options.length * 2 + nonInteractiveLabelCount; diff --git a/superset-frontend/packages/superset-ui-core/src/components/TableCollection/utils.tsx b/superset-frontend/packages/superset-ui-core/src/components/TableCollection/utils.tsx index bc4cf2fd065..182ecf9b94c 100644 --- a/superset-frontend/packages/superset-ui-core/src/components/TableCollection/utils.tsx +++ b/superset-frontend/packages/superset-ui-core/src/components/TableCollection/utils.tsx @@ -121,7 +121,7 @@ export function mapColumns( column, }); } - return val; + return val as ReactNode; }, className: column.className, }; diff --git a/superset-frontend/packages/superset-ui-core/src/components/TruncatedList/index.tsx b/superset-frontend/packages/superset-ui-core/src/components/TruncatedList/index.tsx index 9b6653a7511..39039833353 100644 --- a/superset-frontend/packages/superset-ui-core/src/components/TruncatedList/index.tsx +++ b/superset-frontend/packages/superset-ui-core/src/components/TruncatedList/index.tsx @@ -97,8 +97,8 @@ const StyledPlus = styled.span` export default function TruncatedList({ items, - renderVisibleItem = item => item, - renderTooltipItem = item => item, + renderVisibleItem = item => item as ReactNode, + renderTooltipItem = item => item as ReactNode, getKey = item => item as unknown as Key, maxLinks = 20, }: TruncatedListProps) { diff --git a/superset-frontend/packages/superset-ui-core/src/ui-overrides/types.ts b/superset-frontend/packages/superset-ui-core/src/ui-overrides/types.ts index cca2aed8809..1cd892c69c5 100644 --- a/superset-frontend/packages/superset-ui-core/src/ui-overrides/types.ts +++ b/superset-frontend/packages/superset-ui-core/src/ui-overrides/types.ts @@ -249,7 +249,8 @@ export type Extensions = Partial<{ 'navbar.right-menu.item.icon': ComponentType; 'navbar.right': ComponentType; 'report-modal.dropdown.item.icon': ComponentType; - 'root.context.provider': ComponentType; + 'root.context.provider': ComponentType<{ children?: ReactNode }>; + 'welcome.message': ComponentType; 'welcome.banner': ComponentType; 'welcome.main.replacement': ComponentType; diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/CategoricalDeckGLContainer.tsx b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/CategoricalDeckGLContainer.tsx index e032aefe282..50657827a14 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/CategoricalDeckGLContainer.tsx +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/CategoricalDeckGLContainer.tsx @@ -139,7 +139,8 @@ const CategoricalDeckGLContainer = (props: CategoricalDeckGLContainerProps) => { const setTooltip = useCallback((tooltip: TooltipProps['tooltip']) => { const { current } = containerRef; if (current) { - current.setTooltip(tooltip); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (current as any).setTooltip(tooltip); } }, []); diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/DeckGLContainer.tsx b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/DeckGLContainer.tsx index d558532298d..d24334a2ed2 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/DeckGLContainer.tsx +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/DeckGLContainer.tsx @@ -200,5 +200,5 @@ export const DeckGLContainerStyledWrapper = styled(DeckGLContainer)` `; export type DeckGLContainerHandle = typeof DeckGLContainer & { - setTooltip: (tooltip: ReactNode) => void; + setTooltip: (tooltip: TooltipProps['tooltip']) => void; }; diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.test.ts b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.test.ts index 1dda87eb046..9494b17e14f 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.test.ts +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.test.ts @@ -97,10 +97,10 @@ describe('getAggFunc', () => { }); describe('commonLayerProps', () => { - const mockSetTooltip = jest.fn(); + const mockSetTooltip = jest.fn() as any; const mockSetTooltipContent = jest.fn( () => (o: JsonObject) => `Tooltip for ${o}`, - ); + ) as any; const mockOnSelect = jest.fn(); test('returns correct props when js_tooltip is provided', () => { diff --git a/superset-frontend/plugins/plugin-chart-ag-grid-table/src/renderers/TextCellRenderer.tsx b/superset-frontend/plugins/plugin-chart-ag-grid-table/src/renderers/TextCellRenderer.tsx index 9b24fa3cd33..36bff81b4bd 100644 --- a/superset-frontend/plugins/plugin-chart-ag-grid-table/src/renderers/TextCellRenderer.tsx +++ b/superset-frontend/plugins/plugin-chart-ag-grid-table/src/renderers/TextCellRenderer.tsx @@ -70,5 +70,5 @@ export const TextCellRenderer = (params: CellRendererProps) => { } } - return
{valueFormatted ?? value}
; + return
{valueFormatted ?? (value instanceof Date ? value.toISOString() : value)}
; }; diff --git a/superset-frontend/plugins/plugin-chart-cartodiagram/test/components/chartLayer.test.ts b/superset-frontend/plugins/plugin-chart-cartodiagram/test/components/chartLayer.test.ts index d35326e23dd..230486599e1 100644 --- a/superset-frontend/plugins/plugin-chart-cartodiagram/test/components/chartLayer.test.ts +++ b/superset-frontend/plugins/plugin-chart-cartodiagram/test/components/chartLayer.test.ts @@ -41,6 +41,11 @@ describe('ChartLayer', () => { chartLayer.charts = [ { htmlElement: document.createElement('div'), + root: { render: jest.fn(), unmount: jest.fn() } as any, + coordinate: [0, 0], + width: 100, + height: 100, + feature: {}, }, ]; diff --git a/superset-frontend/spec/helpers/testing-library.tsx b/superset-frontend/spec/helpers/testing-library.tsx index 2645e68feee..e97a5ce85a7 100644 --- a/superset-frontend/spec/helpers/testing-library.tsx +++ b/superset-frontend/spec/helpers/testing-library.tsx @@ -97,6 +97,7 @@ export function createWrapper(options?: Options) { } if (useDnd) { + // @ts-expect-error react-dnd types not updated for React 18 result = {result}; } diff --git a/superset-frontend/src/SqlLab/components/AppLayout/index.tsx b/superset-frontend/src/SqlLab/components/AppLayout/index.tsx index 7059ade1435..0e2722f98fc 100644 --- a/superset-frontend/src/SqlLab/components/AppLayout/index.tsx +++ b/superset-frontend/src/SqlLab/components/AppLayout/index.tsx @@ -65,7 +65,7 @@ const ContentWrapper = styled.div` overflow: auto; `; -const AppLayout: React.FC = ({ children }) => { +const AppLayout: React.FC<{ children?: React.ReactNode }> = ({ children }) => { const queryEditorId = useSelector( ({ sqlLab: { tabHistory } }) => tabHistory.slice(-1)[0], ); diff --git a/superset-frontend/src/SqlLab/components/EditorWrapper/useAnnotations.test.ts b/superset-frontend/src/SqlLab/components/EditorWrapper/useAnnotations.test.ts index 4f34ed36e7c..ee19021fd7c 100644 --- a/superset-frontend/src/SqlLab/components/EditorWrapper/useAnnotations.test.ts +++ b/superset-frontend/src/SqlLab/components/EditorWrapper/useAnnotations.test.ts @@ -17,7 +17,7 @@ * under the License. */ import fetchMock from 'fetch-mock'; -import { act, renderHook } from '@testing-library/react'; +import { act, renderHook, waitFor } from '@testing-library/react'; import { COMMON_ERR_MESSAGES } from '@superset-ui/core'; import { createWrapper, @@ -121,7 +121,7 @@ test('skips fetching validation if validator is undefined', () => { }); test('returns validation if validator is configured', async () => { - const { result, waitFor } = initialize(true); + const { result } = initialize(true); await waitFor(() => expect(fetchMock.callHistory.calls(queryValidationApiRoute)).toHaveLength( 1, @@ -143,7 +143,7 @@ test('returns server error description', async () => { fetchMock.post(queryValidationApiRoute, { throws: new Error(errorMessage), }); - const { result, waitFor } = initialize(true); + const { result } = initialize(true); await waitFor( () => expect(result.current.data).toEqual([ @@ -164,7 +164,7 @@ test('returns session expire description when CSRF token expired', async () => { fetchMock.post(queryValidationApiRoute, { throws: new Error(errorMessage), }); - const { result, waitFor } = initialize(true); + const { result } = initialize(true); await waitFor( () => expect(result.current.data).toEqual([ diff --git a/superset-frontend/src/SqlLab/components/EditorWrapper/useKeywords.test.ts b/superset-frontend/src/SqlLab/components/EditorWrapper/useKeywords.test.ts index c7a87fe3fa5..1d8431372c5 100644 --- a/superset-frontend/src/SqlLab/components/EditorWrapper/useKeywords.test.ts +++ b/superset-frontend/src/SqlLab/components/EditorWrapper/useKeywords.test.ts @@ -17,7 +17,7 @@ * under the License. */ import fetchMock from 'fetch-mock'; -import { act, renderHook } from '@testing-library/react'; +import { act, renderHook, waitFor } from '@testing-library/react'; import { getExtensionsRegistry } from '@superset-ui/core'; import { createWrapper, @@ -104,7 +104,7 @@ test('returns keywords including fetched function_names data', async () => { const dbFunctionNamesApiRoute = `glob:*/api/v1/database/${expectDbId}/function_names/`; fetchMock.get(dbFunctionNamesApiRoute, fakeFunctionNamesApiResult); - const { result, waitFor } = renderHook( + const { result } = renderHook( () => useKeywords({ queryEditorId: 'testqueryid', @@ -241,7 +241,7 @@ test('returns column keywords among selected tables', async () => { ); }); - const { result, waitFor } = renderHook( + const { result } = renderHook( () => useKeywords({ queryEditorId: expectQueryEditorId, @@ -317,7 +317,7 @@ test('returns long keywords with detail', async () => { ), ); }); - const { result, waitFor } = renderHook( + const { result } = renderHook( () => useKeywords({ queryEditorId: 'testqueryid', diff --git a/superset-frontend/src/SqlLab/components/KeyboardShortcutButton/index.tsx b/superset-frontend/src/SqlLab/components/KeyboardShortcutButton/index.tsx index d9d4e1ce6b6..a2ced1f6c19 100644 --- a/superset-frontend/src/SqlLab/components/KeyboardShortcutButton/index.tsx +++ b/superset-frontend/src/SqlLab/components/KeyboardShortcutButton/index.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { FC } from 'react'; +import { FC, ReactNode } from 'react'; import { t } from '@apache-superset/core/translation'; import { styled, css } from '@apache-superset/core/theme'; import { ModalTrigger } from '@superset-ui/core/components'; @@ -92,7 +92,7 @@ const ShortcutCode = styled.code` padding: ${({ theme }) => `${theme.sizeUnit}px ${theme.sizeUnit * 2}px`}; `; -const KeyboardShortcutButton: FC<{}> = ({ children }) => ( +const KeyboardShortcutButton: FC<{ children?: ReactNode }> = ({ children }) => ( { +const PopEditorTab: React.FC<{ children?: React.ReactNode }> = ({ children }) => { const [isLoading, setIsLoading] = useState(false); const [queryEditorId, setQueryEditorId] = useState(); const { requestedQuery } = useLocationState(); diff --git a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx index 2ba0985aed7..7957b38139b 100644 --- a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx +++ b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx @@ -52,10 +52,10 @@ interface QueryTableQuery extends Omit< QueryResponse, 'state' | 'sql' | 'progress' | 'results' | 'duration' | 'started' > { - state?: Record; - sql?: Record; - progress?: Record; - results?: Record; + state?: ReactNode; + sql?: ReactNode; + progress?: ReactNode; + results?: ReactNode; duration?: ReactNode; started?: ReactNode; } @@ -265,7 +265,7 @@ const QueryTable = ({ buttonStyle="link" onClick={() => onUserClicked(q.userId)} > - {q.user} + {q.user as ReactNode} ); q.db = ( @@ -274,7 +274,7 @@ const QueryTable = ({ buttonStyle="link" onClick={() => onDbClicked(q.dbId)} > - {q.db} + {q.db as ReactNode} ); q.started = ( diff --git a/superset-frontend/src/SqlLab/components/RunQueryActionButton/index.tsx b/superset-frontend/src/SqlLab/components/RunQueryActionButton/index.tsx index 4353951a715..9999c884487 100644 --- a/superset-frontend/src/SqlLab/components/RunQueryActionButton/index.tsx +++ b/superset-frontend/src/SqlLab/components/RunQueryActionButton/index.tsx @@ -16,13 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -import { useMemo, FC, ReactElement } from 'react'; +import { useMemo, FC, ReactElement, type ReactNode } from 'react'; import { t } from '@apache-superset/core/translation'; import { styled, useTheme, SupersetTheme } from '@apache-superset/core/theme'; import { Button, DropdownButton } from '@superset-ui/core/components'; -import { IconType, Icons } from '@superset-ui/core/components/Icons'; +import { Icons } from '@superset-ui/core/components/Icons'; import { detectOS } from 'src/utils/common'; import { QueryButtonProps } from 'src/SqlLab/types'; import useQueryEditor from 'src/SqlLab/hooks/useQueryEditor'; @@ -45,9 +45,9 @@ const buildTextAndIcon = ( shouldShowStopButton: boolean, selectedText: string | undefined, theme: SupersetTheme, -): { text: string; icon?: IconType } => { +): { text: string; icon?: ReactNode } => { let text = t('Run'); - let icon: IconType | undefined = ; + let icon: ReactNode = ; if (selectedText) { text = t('Run selection'); icon = ; diff --git a/superset-frontend/src/chartCustomizations/components/DeckglLayerVisibility/useDeckLayerMetadata.test.ts b/superset-frontend/src/chartCustomizations/components/DeckglLayerVisibility/useDeckLayerMetadata.test.ts index 71f584c85a6..5bb5858f1a4 100644 --- a/superset-frontend/src/chartCustomizations/components/DeckglLayerVisibility/useDeckLayerMetadata.test.ts +++ b/superset-frontend/src/chartCustomizations/components/DeckglLayerVisibility/useDeckLayerMetadata.test.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { renderHook } from '@testing-library/react'; +import { renderHook, waitFor } from '@testing-library/react'; import { SupersetClient } from '@superset-ui/core'; import { useDeckLayerMetadata } from './useDeckLayerMetadata'; @@ -52,15 +52,16 @@ test('fetches layer metadata successfully', async () => { }; mockSupersetClientGet.mockResolvedValue(mockResponse); - const { result, waitForNextUpdate } = renderHook(() => + const { result } = renderHook(() => useDeckLayerMetadata([1, 2]), ); expect(result.current.isLoading).toBe(true); - await waitForNextUpdate(); + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + }); - expect(result.current.isLoading).toBe(false); expect(result.current.layers).toEqual([ { sliceId: 1, name: 'Layer 1', type: 'deck_scatter' }, { sliceId: 2, name: 'Layer 2', type: 'deck_arc' }, @@ -75,13 +76,14 @@ test('handles API error and returns fallback layers', async () => { const errorMessage = 'Network error'; mockSupersetClientGet.mockRejectedValue(new Error(errorMessage)); - const { result, waitForNextUpdate } = renderHook(() => + const { result } = renderHook(() => useDeckLayerMetadata([1, 2, 3]), ); - await waitForNextUpdate(); + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + }); - expect(result.current.isLoading).toBe(false); expect(result.current.error).toBe(errorMessage); expect(result.current.layers).toEqual([ { sliceId: 1, name: 'Layer 1', type: 'unknown' }, @@ -93,13 +95,14 @@ test('handles API error and returns fallback layers', async () => { test('handles non-Error object rejection', async () => { mockSupersetClientGet.mockRejectedValue('String error'); - const { result, waitForNextUpdate } = renderHook(() => + const { result } = renderHook(() => useDeckLayerMetadata([1]), ); - await waitForNextUpdate(); + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + }); - expect(result.current.isLoading).toBe(false); expect(result.current.error).toBe('Unknown error'); expect(result.current.layers).toEqual([ { sliceId: 1, name: 'Layer 1', type: 'unknown' }, @@ -125,22 +128,25 @@ test('refetches when sliceIds change', async () => { .mockResolvedValueOnce(mockResponse1) .mockResolvedValueOnce(mockResponse2); - const { result, rerender, waitForNextUpdate } = renderHook( + const { result, rerender } = renderHook( ({ ids }) => useDeckLayerMetadata(ids), { initialProps: { ids: [1] }, }, ); - await waitForNextUpdate(); + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + }); - expect(result.current.isLoading).toBe(false); expect(result.current.layers).toHaveLength(1); expect(result.current.layers[0].sliceId).toBe(1); rerender({ ids: [2, 3] }); - await waitForNextUpdate(); + await waitFor(() => { + expect(result.current.layers).toHaveLength(2); + }); expect(result.current.isLoading).toBe(false); expect(result.current.layers).toHaveLength(2); @@ -157,13 +163,14 @@ test('handles empty result from API', async () => { }; mockSupersetClientGet.mockResolvedValue(mockResponse); - const { result, waitForNextUpdate } = renderHook(() => + const { result } = renderHook(() => useDeckLayerMetadata([1, 2]), ); - await waitForNextUpdate(); + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + }); - expect(result.current.isLoading).toBe(false); expect(result.current.layers).toEqual([]); expect(result.current.error).toBe(null); }); @@ -176,16 +183,16 @@ test('does not refetch when sliceIds array has same values', async () => { }; mockSupersetClientGet.mockResolvedValue(mockResponse); - const { result, rerender, waitForNextUpdate } = renderHook( + const { result, rerender } = renderHook( ({ ids }) => useDeckLayerMetadata(ids), { initialProps: { ids: [1] }, }, ); - await waitForNextUpdate(); - - expect(result.current.isLoading).toBe(false); + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + }); const callCount = mockSupersetClientGet.mock.calls.length; diff --git a/superset-frontend/src/components/Chart/Chart.tsx b/superset-frontend/src/components/Chart/Chart.tsx index a7e38aa61a4..0714ffd50fd 100644 --- a/superset-frontend/src/components/Chart/Chart.tsx +++ b/superset-frontend/src/components/Chart/Chart.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { PureComponent } from 'react'; +import { ErrorInfo, PureComponent } from 'react'; import { logging } from '@apache-superset/core/utils'; import { t } from '@apache-superset/core/translation'; import { @@ -235,16 +235,13 @@ class Chart extends PureComponent { ); } - handleRenderContainerFailure( - error: Error, - info: { componentStack: string } | null, - ) { + handleRenderContainerFailure(error: Error, info: ErrorInfo) { const { actions, chartId } = this.props; logging.warn(error); actions.chartRenderingFailed( error.toString(), chartId, - info ? info.componentStack : null, + info?.componentStack ?? null, ); actions.logEvent(LOG_ACTIONS_RENDER_CHART, { diff --git a/superset-frontend/src/components/Chart/DrillDetail/DrillDetailTableControls.tsx b/superset-frontend/src/components/Chart/DrillDetail/DrillDetailTableControls.tsx index f1c7e23af39..31bb2dd605f 100644 --- a/superset-frontend/src/components/Chart/DrillDetail/DrillDetailTableControls.tsx +++ b/superset-frontend/src/components/Chart/DrillDetail/DrillDetailTableControls.tsx @@ -109,7 +109,7 @@ export default function TableControls({ > {colName} - {val} + {String(val)} ))} diff --git a/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.tsx b/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.tsx index 6150927ca6a..2a5af624cb6 100644 --- a/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.tsx +++ b/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.tsx @@ -737,7 +737,7 @@ function ColumnCollectionTable({ {v} ), - type: d => (d ? : null), + type: d => (d ? : null), advanced_data_type: d => , is_dttm: checkboxGenerator, filterable: checkboxGenerator, @@ -766,7 +766,7 @@ function ColumnCollectionTable({ {v} ), - type: d => (d ? : null), + type: d => (d ? : null), is_dttm: checkboxGenerator, filterable: checkboxGenerator, groupby: checkboxGenerator, diff --git a/superset-frontend/src/components/DynamicPlugins/index.tsx b/superset-frontend/src/components/DynamicPlugins/index.tsx index 22601f29c65..9c134d83df6 100644 --- a/superset-frontend/src/components/DynamicPlugins/index.tsx +++ b/superset-frontend/src/components/DynamicPlugins/index.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { useContext, useEffect, useReducer, createContext, FC } from 'react'; +import { useContext, useEffect, useReducer, createContext, FC, ReactNode } from 'react'; import { ChartMetadata, @@ -122,7 +122,7 @@ const sharedModules = { '@superset-ui/core': () => import('@superset-ui/core'), }; -export const DynamicPluginProvider: FC = ({ children }) => { +export const DynamicPluginProvider: FC<{ children?: ReactNode }> = ({ children }) => { const [pluginState, dispatch] = useReducer( pluginContextReducer, dummyPluginContext, diff --git a/superset-frontend/src/dashboard/components/gridComponents/Header/Header.test.tsx b/superset-frontend/src/dashboard/components/gridComponents/Header/Header.test.tsx index fd2dc041453..5c13f038f03 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Header/Header.test.tsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Header/Header.test.tsx @@ -74,6 +74,7 @@ describe('Header', () => { function setup(overrideProps: Partial = {}) { return render( + {/* @ts-expect-error react-dnd types not updated for React 18 */}
diff --git a/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.test.tsx b/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.test.tsx index d903eb3e79f..47ed749bbcb 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.test.tsx +++ b/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.test.tsx @@ -36,6 +36,7 @@ describe('DraggableNewComponent', () => { function setup(overrideProps: Record = {}) { return render( + // @ts-expect-error react-dnd types not updated for React 18 , diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.tsx index 23348694fdf..c37fbac73d4 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.tsx @@ -121,7 +121,9 @@ export default function getControlItemsMap({ initialValue={initColumn} label={ - {mainControlItem.config?.label || t('Column')} + {typeof mainControlItem.config?.label === 'function' + ? (mainControlItem.config.label as Function)() + : mainControlItem.config?.label || t('Column')} } rules={[ @@ -219,11 +221,14 @@ export default function getControlItemsMap({ }} > <> - {controlItem.config.label}  + {typeof controlItem.config.label === 'function' + ? (controlItem.config.label as Function)() + : controlItem.config.label} +   {controlItem.config.description && ( )} diff --git a/superset-frontend/src/dashboard/components/resizable/ResizableContainer.tsx b/superset-frontend/src/dashboard/components/resizable/ResizableContainer.tsx index d76070458d5..9004bc735df 100644 --- a/superset-frontend/src/dashboard/components/resizable/ResizableContainer.tsx +++ b/superset-frontend/src/dashboard/components/resizable/ResizableContainer.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { useState, useCallback, useMemo } from 'react'; +import { useState, useCallback, useMemo, ReactNode } from 'react'; import { ResizeCallback, ResizeStartCallback, Resizable } from 're-resizable'; import cx from 'classnames'; import { css, styled } from '@apache-superset/core/theme'; @@ -33,7 +33,7 @@ const proxyToInfinity = Number.MAX_VALUE; export interface ResizableContainerProps { id: string; - children?: object; + children?: ReactNode; adjustableWidth?: boolean; adjustableHeight?: boolean; gutterWidth?: number; diff --git a/superset-frontend/src/embedded/EmbeddedContextProviders.tsx b/superset-frontend/src/embedded/EmbeddedContextProviders.tsx index 9ea9c0744ab..9c0a8f0dcd7 100644 --- a/superset-frontend/src/embedded/EmbeddedContextProviders.tsx +++ b/superset-frontend/src/embedded/EmbeddedContextProviders.tsx @@ -58,7 +58,7 @@ export const getThemeController = (): ThemeController => themeController; const extensionsRegistry = getExtensionsRegistry(); -export const EmbeddedContextProviders: React.FC = ({ children }) => { +export const EmbeddedContextProviders: React.FC<{ children?: React.ReactNode }> = ({ children }) => { const RootContextProviderExtension = extensionsRegistry.get( 'root.context.provider', ); @@ -66,6 +66,7 @@ export const EmbeddedContextProviders: React.FC = ({ children }) => { return ( + {/* @ts-expect-error react-dnd types not updated for React 18 */} diff --git a/superset-frontend/src/explore/components/ControlHeader.tsx b/superset-frontend/src/explore/components/ControlHeader.tsx index d4563a8dbbd..a2bbf047c31 100644 --- a/superset-frontend/src/explore/components/ControlHeader.tsx +++ b/superset-frontend/src/explore/components/ControlHeader.tsx @@ -37,6 +37,8 @@ export type ControlHeaderProps = { tooltipOnClick?: () => void; warning?: string; danger?: string; + // Allow extra props from control spread patterns (e.g. {...this.props}) + [key: string]: unknown; }; const iconStyles = css` diff --git a/superset-frontend/src/explore/components/ExploreChartPanel/index.tsx b/superset-frontend/src/explore/components/ExploreChartPanel/index.tsx index c692e65bfd1..0f13b5c9083 100644 --- a/superset-frontend/src/explore/components/ExploreChartPanel/index.tsx +++ b/superset-frontend/src/explore/components/ExploreChartPanel/index.tsx @@ -51,7 +51,7 @@ import { ExploreAlert } from '../ExploreAlert'; import useResizeDetectorByObserver from './useResizeDetectorByObserver'; const extensionsRegistry = getExtensionsRegistry(); -const DefaultHeader: React.FC = ({ children }) => <>{children}; +const DefaultHeader: React.FC<{ children?: React.ReactNode }> = ({ children }) => <>{children}; export interface ExploreChartPanelProps { actions: { diff --git a/superset-frontend/src/explore/components/ExploreContainer/index.tsx b/superset-frontend/src/explore/components/ExploreContainer/index.tsx index fbac64cf1f6..14150a7d60c 100644 --- a/superset-frontend/src/explore/components/ExploreContainer/index.tsx +++ b/superset-frontend/src/explore/components/ExploreContainer/index.tsx @@ -22,6 +22,7 @@ import { useState, Dispatch, FC, + ReactNode, useReducer, } from 'react'; @@ -60,7 +61,7 @@ const reducer = (state: DropzoneSet = {}, action: Action) => { return state; }; -const ExploreContainer: FC<{}> = ({ children }) => { +const ExploreContainer: FC<{ children?: ReactNode }> = ({ children }) => { const dragDropManager = useDragDropManager(); const [dragging, setDragging] = useState( dragDropManager.getMonitor().isDragging(), diff --git a/superset-frontend/src/explore/components/StashFormDataContainer/index.tsx b/superset-frontend/src/explore/components/StashFormDataContainer/index.tsx index 5761bef0a6e..a35d77f83c8 100644 --- a/superset-frontend/src/explore/components/StashFormDataContainer/index.tsx +++ b/superset-frontend/src/explore/components/StashFormDataContainer/index.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { useEffect, FC } from 'react'; +import { useEffect, FC, ReactNode } from 'react'; import { useDispatch } from 'react-redux'; import { setStashFormData } from 'src/explore/actions/exploreActions'; @@ -25,6 +25,7 @@ import useEffectEvent from 'src/hooks/useEffectEvent'; type Props = { shouldStash: boolean; fieldNames: ReadonlyArray; + children?: ReactNode; }; const StashFormDataContainer: FC = ({ diff --git a/superset-frontend/src/explore/components/controls/CustomListItem/index.tsx b/superset-frontend/src/explore/components/controls/CustomListItem/index.tsx index 6c848cba55d..ace2ac491be 100644 --- a/superset-frontend/src/explore/components/controls/CustomListItem/index.tsx +++ b/superset-frontend/src/explore/components/controls/CustomListItem/index.tsx @@ -16,14 +16,17 @@ * specific language governing permissions and limitations * under the License. */ +import { forwardRef, type ReactNode } from 'react'; import { useTheme } from '@apache-superset/core/theme'; import { List, type ListItemProps } from '@superset-ui/core/components'; export interface CustomListItemProps extends ListItemProps { selectable: boolean; + children?: ReactNode; } -export default function CustomListItem(props: CustomListItemProps) { +const CustomListItem = forwardRef( + function CustomListItem(props, ref) { const { selectable, children, ...rest } = props; const theme = useTheme(); const css: Record | string>> = { @@ -47,8 +50,11 @@ export default function CustomListItem(props: CustomListItemProps) { } return ( - + {children} ); -} +}, +); + +export default CustomListItem; diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterPopoverTrigger/index.tsx b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterPopoverTrigger/index.tsx index 115fb9449a7..57386eca635 100644 --- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterPopoverTrigger/index.tsx +++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterPopoverTrigger/index.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { PureComponent } from 'react'; +import { PureComponent, ReactNode } from 'react'; import { OptionSortType } from 'src/explore/types'; import AdhocFilterEditPopover from 'src/explore/components/controls/FilterControl/AdhocFilterEditPopover'; import AdhocFilter from 'src/explore/components/controls/FilterControl/AdhocFilter'; @@ -37,6 +37,7 @@ interface AdhocFilterPopoverTriggerProps { togglePopover?: (visible: boolean) => void; closePopover?: () => void; requireSave?: boolean; + children?: ReactNode; } interface AdhocFilterPopoverTriggerState { diff --git a/superset-frontend/src/features/home/SubMenu.tsx b/superset-frontend/src/features/home/SubMenu.tsx index f71892fb501..78a4eeffb84 100644 --- a/superset-frontend/src/features/home/SubMenu.tsx +++ b/superset-frontend/src/features/home/SubMenu.tsx @@ -36,7 +36,6 @@ import { type OnClickHandler, } from '@superset-ui/core/components'; import { Icons } from '@superset-ui/core/components/Icons'; -import { IconType } from '@superset-ui/core/components/Icons/types'; import { MenuObjectProps } from 'src/types/bootstrapTypes'; import { Typography } from '@superset-ui/core/components/Typography'; @@ -149,7 +148,7 @@ export interface ButtonProps { 'data-test'?: string; buttonStyle: 'primary' | 'secondary' | 'dashed' | 'link' | 'tertiary'; loading?: boolean; - icon?: IconType; + icon?: ReactNode; } export interface SubMenuProps { @@ -164,6 +163,7 @@ export interface SubMenuProps { color?: string; dropDownLinks?: Array; backgroundColor?: string; + children?: ReactNode; } const { SubMenu } = MainNav; diff --git a/superset-frontend/src/features/home/types.ts b/superset-frontend/src/features/home/types.ts index a59e9fcd858..633f0bffcfa 100644 --- a/superset-frontend/src/features/home/types.ts +++ b/superset-frontend/src/features/home/types.ts @@ -17,6 +17,7 @@ * under the License. */ +import { ReactNode } from 'react'; import { Filter } from 'src/views/CRUD/types'; import { NavBarProps, MenuObjectProps } from 'src/types/bootstrapTypes'; @@ -45,6 +46,7 @@ export interface RightMenuProps { text: string; color: string; }; + children?: ReactNode; } export enum GlobalMenuDataOptions { diff --git a/superset-frontend/src/features/reports/ReportModal/index.tsx b/superset-frontend/src/features/reports/ReportModal/index.tsx index 425867ced55..e313d5c68ae 100644 --- a/superset-frontend/src/features/reports/ReportModal/index.tsx +++ b/superset-frontend/src/features/reports/ReportModal/index.tsx @@ -23,6 +23,7 @@ import { useCallback, useMemo, ChangeEvent, + ReactNode, } from 'react'; import { t } from '@apache-superset/core/translation'; @@ -366,7 +367,7 @@ function ReportModal({ }} onError={setCronError} /> - {cronError} + {cronError as ReactNode}
TimezoneHeaderStyle(theme)} diff --git a/superset-frontend/src/features/roles/RoleListEditModal.tsx b/superset-frontend/src/features/roles/RoleListEditModal.tsx index 42581ecfe2b..100b17e6f11 100644 --- a/superset-frontend/src/features/roles/RoleListEditModal.tsx +++ b/superset-frontend/src/features/roles/RoleListEditModal.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { useEffect, useMemo, useRef, useState } from 'react'; +import { ReactNode, useEffect, useMemo, useRef, useState } from 'react'; import { t } from '@apache-superset/core/translation'; import Tabs from '@superset-ui/core/components/Tabs'; import { RoleObject } from 'src/pages/RolesList'; @@ -330,7 +330,7 @@ function RoleListEditModal({ initialValues={initialValues} requiredFields={['roleName']} > - {(form: FormInstance) => { + {((form: FormInstance) => { formRef.current = form; return ( @@ -368,7 +368,7 @@ function RoleListEditModal({ ); - }} + }) as unknown as ReactNode} ); } diff --git a/superset-frontend/src/features/users/UserListModal.tsx b/superset-frontend/src/features/users/UserListModal.tsx index 9b01a79e62a..fea19d7d753 100644 --- a/superset-frontend/src/features/users/UserListModal.tsx +++ b/superset-frontend/src/features/users/UserListModal.tsx @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import { ReactNode } from 'react'; import { t } from '@apache-superset/core/translation'; import { ModalTitleWithIcon } from 'src/components/ModalTitleWithIcon'; import { useToasts } from 'src/components/MessageToasts/withToasts'; @@ -133,7 +134,7 @@ function UserListModal({ requiredFields={requiredFields} initialValues={initialValues} > - {(form: FormInstance) => ( + {((form: FormInstance) => ( <> )} - )} + )) as unknown as ReactNode} ); } diff --git a/superset-frontend/src/hooks/apiResources/dashboards.test.ts b/superset-frontend/src/hooks/apiResources/dashboards.test.ts index 5528d35a006..9eb0ec8a73a 100644 --- a/superset-frontend/src/hooks/apiResources/dashboards.test.ts +++ b/superset-frontend/src/hooks/apiResources/dashboards.test.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { renderHook } from '@testing-library/react'; +import { renderHook, waitFor } from '@testing-library/react'; import fetchMock from 'fetch-mock'; import { useDashboard, useDashboardDatasets } from './dashboards'; @@ -31,8 +31,12 @@ test('useDashboard excludes thumbnail_url from request', async () => { }, }); - const { waitForNextUpdate } = renderHook(() => useDashboard(5)); - await waitForNextUpdate(); + renderHook(() => useDashboard(5)); + + await waitFor(() => { + const calledUrl = fetchMock.callHistory.lastCall()?.url ?? ''; + expect(calledUrl).toContain('?q='); + }); const calledUrl = fetchMock.callHistory.lastCall()?.url ?? ''; expect(calledUrl).toContain('?q='); @@ -82,10 +86,9 @@ describe('useDashboardDatasets', () => { result: mockDatasets, }); - const { result, waitForNextUpdate } = renderHook(() => + const { result } = renderHook(() => useDashboardDatasets(1), ); - await waitForNextUpdate(); const expectedContent = [ { @@ -106,6 +109,8 @@ describe('useDashboardDatasets', () => { }, }, ]; - expect(result.current.result).toEqual(expectedContent); + await waitFor(() => { + expect(result.current.result).toEqual(expectedContent); + }); }); }); diff --git a/superset-frontend/src/hooks/apiResources/databaseFunctions.test.ts b/superset-frontend/src/hooks/apiResources/databaseFunctions.test.ts index 3021f1147e2..4ce4970a40f 100644 --- a/superset-frontend/src/hooks/apiResources/databaseFunctions.test.ts +++ b/superset-frontend/src/hooks/apiResources/databaseFunctions.test.ts @@ -17,7 +17,7 @@ * under the License. */ import fetchMock from 'fetch-mock'; -import { act, renderHook } from '@testing-library/react'; +import { act, renderHook, waitFor } from '@testing-library/react'; import { createWrapper, defaultStore as store, @@ -45,7 +45,7 @@ beforeEach(() => { }); test('returns api response mapping json result', async () => { - const { result, waitFor } = renderHook( + const { result } = renderHook( () => useDatabaseFunctionsQuery({ dbId: expectDbId, @@ -72,7 +72,7 @@ test('returns api response mapping json result', async () => { }); test('returns cached data without api request', async () => { - const { result, waitFor, rerender } = renderHook( + const { result, rerender } = renderHook( () => useDatabaseFunctionsQuery({ dbId: expectDbId, diff --git a/superset-frontend/src/hooks/apiResources/datasets.test.ts b/superset-frontend/src/hooks/apiResources/datasets.test.ts index 21f41f539cf..88762fb00aa 100644 --- a/superset-frontend/src/hooks/apiResources/datasets.test.ts +++ b/superset-frontend/src/hooks/apiResources/datasets.test.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { renderHook } from '@testing-library/react'; +import { renderHook, waitFor } from '@testing-library/react'; import { Dataset } from 'src/components/Chart/types'; import { cachedSupersetGet, @@ -159,15 +159,16 @@ test('useDatasetDrillInfo fetches dataset drill info successfully', async () => }, } as any); - const { result, waitForNextUpdate } = renderHook(() => + const { result } = renderHook(() => useDatasetDrillInfo(123, 456), ); expect(result.current.status).toBe('loading'); - await waitForNextUpdate(); + await waitFor(() => { + expect(result.current.status).toBe('complete'); + }); - expect(result.current.status).toBe('complete'); expect(result.current.result).toEqual({ ...mockDataset, verbose_map: { @@ -181,13 +182,13 @@ test('useDatasetDrillInfo fetches dataset drill info successfully', async () => test('useDatasetDrillInfo handles network errors', async () => { mockedCachedSupersetGet.mockRejectedValue(new Error('Network error')); - const { result, waitForNextUpdate } = renderHook(() => + const { result } = renderHook(() => useDatasetDrillInfo(123, 456), ); - await waitForNextUpdate(); - - expect(result.current.status).toBe('error'); + await waitFor(() => { + expect(result.current.status).toBe('error'); + }); expect(result.current.result).toBeNull(); expect(result.current.error).toBeInstanceOf(Error); expect(result.current.error?.message).toBe('Network error'); @@ -221,13 +222,13 @@ test('useDatasetDrillInfo extracts dataset ID from string format', async () => { }, } as any); - const { result, waitForNextUpdate } = renderHook(() => + const { result } = renderHook(() => useDatasetDrillInfo('123__table', 456), ); - await waitForNextUpdate(); - - expect(result.current.status).toBe('complete'); + await waitFor(() => { + expect(result.current.status).toBe('complete'); + }); expect(mockedCachedSupersetGet).toHaveBeenCalledWith({ endpoint: '/api/v1/dataset/123/drill_info/?q=(dashboard_id:456)', }); @@ -246,9 +247,11 @@ test('useDatasetDrillInfo does not clear cache on successful fetch', async () => }, } as any); - const { waitForNextUpdate } = renderHook(() => useDatasetDrillInfo(123, 456)); + const { result } = renderHook(() => useDatasetDrillInfo(123, 456)); - await waitForNextUpdate(); + await waitFor(() => { + expect(result.current.status).toBe('complete'); + }); // Cache should NOT be deleted on success expect(mockedSupersetGetCacheDelete).not.toHaveBeenCalled(); @@ -268,13 +271,14 @@ test('useDatasetDrillInfo creates new verbose_map from columns and metrics', asy }, } as any); - const { result, waitForNextUpdate } = renderHook(() => + const { result } = renderHook(() => useDatasetDrillInfo(123, 456), ); - await waitForNextUpdate(); + await waitFor(() => { + expect(result.current.status).toBe('complete'); + }); - expect(result.current.status).toBe('complete'); // Verify verbose_map is created from columns/metrics (existing verbose_map replaced) expect(result.current.result?.verbose_map).toEqual({ col1: 'Column 1', @@ -291,11 +295,13 @@ test('useDatasetDrillInfo handles NaN datasource ID from malformed string', asyn }, } as any); - const { result, waitForNextUpdate } = renderHook(() => + const { result } = renderHook(() => useDatasetDrillInfo('abc', 456), ); - await waitForNextUpdate(); + await waitFor(() => { + expect(result.current.status).toBe('complete'); + }); // Verify hook calls endpoint with NaN (API will handle validation) expect(mockedCachedSupersetGet).toHaveBeenCalledWith({ @@ -322,13 +328,15 @@ test('useDatasetDrillInfo fetches dataset via extension when extension and formD json: { result: mockDataset }, } as any); - const { result, waitForNextUpdate } = renderHook(() => + const { result } = renderHook(() => useDatasetDrillInfo(123, 456, mockFormData), ); expect(result.current.status).toBe('loading'); - await waitForNextUpdate(); + await waitFor(() => { + expect(result.current.status).toBe('complete'); + }); // Verify extension was called with correct arguments expect(mockExtension).toHaveBeenCalledWith(123, mockFormData); @@ -356,14 +364,13 @@ test('useDatasetDrillInfo handles extension throwing error', async () => { mockExtension.mockRejectedValue(extensionError); - const { result, waitForNextUpdate } = renderHook(() => + const { result } = renderHook(() => useDatasetDrillInfo(123, 456, mockFormData), ); - await waitForNextUpdate(); - - // Verify error state - expect(result.current.status).toBe('error'); + await waitFor(() => { + expect(result.current.status).toBe('error'); + }); expect(result.current.result).toBeNull(); expect(result.current.error).toBeInstanceOf(Error); expect(result.current.error?.message).toBe('Extension failed'); @@ -383,14 +390,15 @@ test('useDatasetDrillInfo handles extension returning malformed payload with und // Extension returns undefined instead of expected shape mockExtension.mockResolvedValue(undefined as any); - const { result, waitForNextUpdate } = renderHook(() => + const { result } = renderHook(() => useDatasetDrillInfo(123, 456, mockFormData), ); - await waitForNextUpdate(); + await waitFor(() => { + expect(result.current.status).toBe('complete'); + }); // Hook should handle gracefully and set result with empty verbose_map - expect(result.current.status).toBe('complete'); expect(result.current.result).toEqual({ verbose_map: {} }); expect(result.current.error).toBeNull(); }); @@ -403,14 +411,15 @@ test('useDatasetDrillInfo handles extension returning malformed payload with mis // Extension returns object but missing json.result mockExtension.mockResolvedValue({ json: {} } as any); - const { result, waitForNextUpdate } = renderHook(() => + const { result } = renderHook(() => useDatasetDrillInfo(123, 456, mockFormData), ); - await waitForNextUpdate(); + await waitFor(() => { + expect(result.current.status).toBe('complete'); + }); // Hook should handle gracefully - undefined result gets empty verbose_map - expect(result.current.status).toBe('complete'); expect(result.current.result).toEqual({ verbose_map: {} }); expect(result.current.error).toBeNull(); }); @@ -430,11 +439,13 @@ test('useDatasetDrillInfo falls back to REST API when extension exists but formD json: { result: mockDataset }, } as any); - const { result, waitForNextUpdate } = renderHook( + const { result } = renderHook( () => useDatasetDrillInfo(123, 456, undefined), // formData is undefined ); - await waitForNextUpdate(); + await waitFor(() => { + expect(result.current.status).toBe('complete'); + }); // Should use REST API, NOT extension expect(mockedCachedSupersetGet).toHaveBeenCalledWith({ diff --git a/superset-frontend/src/hooks/apiResources/queries.test.ts b/superset-frontend/src/hooks/apiResources/queries.test.ts index 1e97848421a..234fb0c226c 100644 --- a/superset-frontend/src/hooks/apiResources/queries.test.ts +++ b/superset-frontend/src/hooks/apiResources/queries.test.ts @@ -18,7 +18,7 @@ */ import rison from 'rison'; import fetchMock from 'fetch-mock'; -import { act, renderHook } from '@testing-library/react'; +import { act, renderHook, waitFor } from '@testing-library/react'; import { createWrapper, defaultStore as store, @@ -76,7 +76,7 @@ test('returns api response mapping camelCase keys', async () => { const editorId = '23'; const editorQueryApiRoute = `glob:*/api/v1/query/?q=*`; fetchMock.get(editorQueryApiRoute, fakeApiResult); - const { result, waitFor } = renderHook( + const { result } = renderHook( () => useEditorQueriesQuery({ editorId }), { wrapper: createWrapper({ @@ -124,7 +124,7 @@ test('merges paginated results', async () => { const editorId = '23'; const editorQueryApiRoute = `glob:*/api/v1/query/?q=*`; fetchMock.get(editorQueryApiRoute, fakeApiResult); - const { waitFor } = renderHook(() => useEditorQueriesQuery({ editorId }), { + renderHook(() => useEditorQueriesQuery({ editorId }), { wrapper: createWrapper({ useRedux: true, store, diff --git a/superset-frontend/src/hooks/apiResources/queryValidations.test.ts b/superset-frontend/src/hooks/apiResources/queryValidations.test.ts index 79f66285043..910b350e05c 100644 --- a/superset-frontend/src/hooks/apiResources/queryValidations.test.ts +++ b/superset-frontend/src/hooks/apiResources/queryValidations.test.ts @@ -17,7 +17,7 @@ * under the License. */ import fetchMock from 'fetch-mock'; -import { act, renderHook } from '@testing-library/react'; +import { act, renderHook, waitFor } from '@testing-library/react'; import { createWrapper, defaultStore as store, @@ -53,7 +53,7 @@ afterEach(() => { test('returns api response mapping json result', async () => { const queryValidationApiRoute = `glob:*/api/v1/database/${expectDbId}/validate_sql/`; fetchMock.post(queryValidationApiRoute, fakeApiResult); - const { result, waitFor } = renderHook( + const { result } = renderHook( () => useQueryValidationsQuery({ dbId: expectDbId, @@ -94,7 +94,7 @@ test('returns api response mapping json result', async () => { test('returns cached data without api request', async () => { const queryValidationApiRoute = `glob:*/api/v1/database/${expectDbId}/validate_sql/`; fetchMock.post(queryValidationApiRoute, fakeApiResult); - const { result, waitFor, rerender } = renderHook( + const { result, rerender } = renderHook( () => useQueryValidationsQuery({ dbId: expectDbId, diff --git a/superset-frontend/src/hooks/apiResources/schemas.test.ts b/superset-frontend/src/hooks/apiResources/schemas.test.ts index f5c61dc4af2..e09c74fa597 100644 --- a/superset-frontend/src/hooks/apiResources/schemas.test.ts +++ b/superset-frontend/src/hooks/apiResources/schemas.test.ts @@ -18,7 +18,7 @@ */ import rison from 'rison'; import fetchMock from 'fetch-mock'; -import { act, renderHook } from '@testing-library/react'; +import { act, renderHook, waitFor } from '@testing-library/react'; import { createWrapper, defaultStore as store, @@ -65,7 +65,7 @@ describe('useSchemas hook', () => { const schemaApiRoute = `glob:*/api/v1/database/${expectDbId}/schemas/*`; fetchMock.get(schemaApiRoute, fakeApiResult); const onSuccess = jest.fn(); - const { result, waitFor } = renderHook( + const { result } = renderHook( () => useSchemas({ dbId: expectDbId, @@ -111,7 +111,7 @@ describe('useSchemas hook', () => { const expectDbId = 'db1'; const schemaApiRoute = `glob:*/api/v1/database/${expectDbId}/schemas/*`; fetchMock.get(schemaApiRoute, fakeApiResult); - const { result, rerender, waitFor } = renderHook( + const { result, rerender } = renderHook( () => useSchemas({ dbId: expectDbId, @@ -137,7 +137,7 @@ describe('useSchemas hook', () => { url.includes(expectDbId) ? fakeApiResult : fakeApiResult2, ); const onSuccess = jest.fn(); - const { result, rerender, waitFor } = renderHook( + const { result, rerender } = renderHook( ({ dbId }) => useSchemas({ dbId, @@ -198,7 +198,7 @@ describe('useSchemas hook', () => { : fakeApiResult2, ); const onSuccess = jest.fn(); - const { result, rerender, waitFor } = renderHook( + const { result, rerender } = renderHook( ({ dbId, catalog }) => useSchemas({ dbId, diff --git a/superset-frontend/src/hooks/apiResources/sqlEditorTabs.test.ts b/superset-frontend/src/hooks/apiResources/sqlEditorTabs.test.ts index 43055f19de9..b12ed831f5e 100644 --- a/superset-frontend/src/hooks/apiResources/sqlEditorTabs.test.ts +++ b/superset-frontend/src/hooks/apiResources/sqlEditorTabs.test.ts @@ -17,7 +17,7 @@ * under the License. */ import fetchMock from 'fetch-mock'; -import { act, renderHook } from '@testing-library/react'; +import { act, renderHook, waitFor } from '@testing-library/react'; import { createWrapper, defaultStore as store, @@ -56,7 +56,7 @@ afterEach(() => { test('puts api request with formData', async () => { const tabStateMutationApiRoute = `glob:*/tabstateview/${expectedQueryEditor.id}`; fetchMock.put(tabStateMutationApiRoute, 200); - const { result, waitFor } = renderHook( + const { result } = renderHook( () => useUpdateSqlEditorTabMutation(), { wrapper: createWrapper({ @@ -108,7 +108,7 @@ test('puts api request with formData', async () => { test('posts activate request with queryEditorId', async () => { const tabStateMutationApiRoute = `glob:*/tabstateview/${expectedQueryEditor.id}/activate`; fetchMock.post(tabStateMutationApiRoute, 200); - const { result, waitFor } = renderHook( + const { result } = renderHook( () => useUpdateCurrentSqlEditorTabMutation(), { wrapper: createWrapper({ @@ -130,7 +130,7 @@ test('posts activate request with queryEditorId', async () => { test('deletes destoryed query editors', async () => { const tabStateMutationApiRoute = `glob:*/tabstateview/${expectedQueryEditor.id}`; fetchMock.delete(tabStateMutationApiRoute, 200); - const { result, waitFor } = renderHook( + const { result } = renderHook( () => useDeleteSqlEditorTabMutation(), { wrapper: createWrapper({ diff --git a/superset-frontend/src/hooks/apiResources/sqlLab.test.ts b/superset-frontend/src/hooks/apiResources/sqlLab.test.ts index ba0e9fb9119..6097098f965 100644 --- a/superset-frontend/src/hooks/apiResources/sqlLab.test.ts +++ b/superset-frontend/src/hooks/apiResources/sqlLab.test.ts @@ -17,7 +17,7 @@ * under the License. */ import fetchMock from 'fetch-mock'; -import { act, renderHook } from '@testing-library/react'; +import { act, renderHook, waitFor } from '@testing-library/react'; import { createWrapper, defaultStore as store, @@ -61,7 +61,7 @@ beforeEach(() => { }); test('returns api response mapping json result', async () => { - const { result, waitFor } = renderHook(() => useSqlLabInitialState(), { + const { result } = renderHook(() => useSqlLabInitialState(), { wrapper: createWrapper({ useRedux: true, store, @@ -89,7 +89,7 @@ test('returns api response mapping json result', async () => { }); test('returns cached data without api request', async () => { - const { result, waitFor, rerender } = renderHook( + const { result, rerender } = renderHook( () => useSqlLabInitialState(), { wrapper: createWrapper({ diff --git a/superset-frontend/src/hooks/apiResources/tables.test.ts b/superset-frontend/src/hooks/apiResources/tables.test.ts index 829e19a84d2..572f5c7ca3a 100644 --- a/superset-frontend/src/hooks/apiResources/tables.test.ts +++ b/superset-frontend/src/hooks/apiResources/tables.test.ts @@ -18,7 +18,7 @@ */ import rison from 'rison'; import fetchMock from 'fetch-mock'; -import { act, renderHook } from '@testing-library/react'; +import { act, renderHook, waitFor } from '@testing-library/react'; import { createWrapper, defaultStore as store, @@ -88,7 +88,7 @@ describe('useTables hook', () => { fetchMock.get(schemaApiRoute, { result: fakeSchemaApiResult, }); - const { result, waitFor } = renderHook( + const { result } = renderHook( () => useTables({ dbId: expectDbId, @@ -139,7 +139,7 @@ describe('useTables hook', () => { fetchMock.get(schemaApiRoute, { result: fakeSchemaApiResult, }); - const { result, waitFor } = renderHook( + const { result } = renderHook( () => useTables({ dbId: expectDbId, @@ -178,7 +178,7 @@ describe('useTables hook', () => { fetchMock.get(`glob:*/api/v1/database/${expectDbId}/schemas/*`, { result: fakeSchemaApiResult, }); - const { result, waitFor } = renderHook( + const { result } = renderHook( () => useTables({ dbId: expectDbId, @@ -209,7 +209,7 @@ describe('useTables hook', () => { fetchMock.get(`glob:*/api/v1/database/${expectDbId}/schemas/*`, { result: fakeSchemaApiResult, }); - const { result, rerender, waitFor } = renderHook( + const { result, rerender } = renderHook( () => useTables({ dbId: expectDbId, @@ -257,7 +257,7 @@ describe('useTables hook', () => { fetchMock.get(`glob:*/api/v1/database/${expectDbId}/schemas/*`, { result: fakeSchemaApiResult, }); - const { result, rerender, waitFor } = renderHook( + const { result, rerender } = renderHook( ({ schema }) => useTables({ dbId: expectDbId, diff --git a/superset-frontend/src/pages/SqlLab/LocationContext.tsx b/superset-frontend/src/pages/SqlLab/LocationContext.tsx index de9539c2f7d..09c7dfb96fd 100644 --- a/superset-frontend/src/pages/SqlLab/LocationContext.tsx +++ b/superset-frontend/src/pages/SqlLab/LocationContext.tsx @@ -31,7 +31,7 @@ const { Provider } = locationContext; const EMPTY_STATE: LocationState = {}; -export const LocationProvider: FC = ({ children }: { children: ReactNode }) => { +export const LocationProvider: FC<{ children?: ReactNode }> = ({ children }) => { const location = useLocation(); if (location.state) { return {children}; diff --git a/superset-frontend/src/views/RootContextProviders.tsx b/superset-frontend/src/views/RootContextProviders.tsx index a4cfb95c42d..0da4780bf26 100644 --- a/superset-frontend/src/views/RootContextProviders.tsx +++ b/superset-frontend/src/views/RootContextProviders.tsx @@ -34,7 +34,7 @@ import querystring from 'query-string'; const themeController = new ThemeController(); const extensionsRegistry = getExtensionsRegistry(); -export const RootContextProviders: React.FC = ({ children }) => { +export const RootContextProviders: React.FC<{ children?: React.ReactNode }> = ({ children }) => { const RootContextProviderExtension = extensionsRegistry.get( 'root.context.provider', ); @@ -42,6 +42,7 @@ export const RootContextProviders: React.FC = ({ children }) => { return ( + {/* @ts-expect-error react-dnd types not updated for React 18 */}