chore: Upgrade to React 18 (#38563)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Evan Rusackas <evan@preset.io>
This commit is contained in:
Mehmet Salih Yavuz
2026-05-04 19:19:36 +03:00
committed by GitHub
parent 28239c18d4
commit 41a22d7918
183 changed files with 5035 additions and 7225 deletions

View File

@@ -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`

View File

@@ -17,7 +17,7 @@
* under the License.
*/
import { renderHook } from '@testing-library/react-hooks';
import { renderHook } from '@testing-library/react';
import { useFilteredTableData } from '.';
const data = [

View File

@@ -17,7 +17,7 @@
* under the License.
*/
import { GenericDataType } from '@apache-superset/core/common';
import { renderHook } from '@testing-library/react-hooks';
import { renderHook } from '@testing-library/react';
import { Constants } from '@superset-ui/core/components';
import { useTableColumns } from '.';

View File

@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { FC, memo, useCallback, useEffect, useMemo, useState } from 'react';
import { useHistory } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import { QueryFormData, JsonObject } from '@superset-ui/core';
@@ -100,7 +100,7 @@ const additionalItemsStyles = (theme: SupersetTheme) => css`
}
`;
export const ExploreChartHeader: FC<ExploreChartHeaderProps> = ({
const ExploreChartHeader: FC<ExploreChartHeaderProps> = ({
dashboardId,
colorScheme: dashboardColorScheme,
slice,
@@ -270,77 +270,112 @@ export const ExploreChartHeader: FC<ExploreChartHeaderProps> = ({
}
}, [showUnsavedChangesModal, shouldForceCloseModal]);
const editableTitleProps = useMemo(
() => ({
title: sliceName ?? '',
canEdit:
!slice ||
canOverwrite ||
(user?.userId !== undefined &&
(slice?.owners || []).includes(user.userId)),
onSave: actions.updateChartTitle,
placeholder: t('Add the name of the chart'),
label: t('Chart title'),
}),
[actions.updateChartTitle, canOverwrite, slice, sliceName, user?.userId],
);
const certificatiedBadgeProps = useMemo(
() => ({
certifiedBy: slice?.certified_by,
details: slice?.certification_details,
}),
[slice?.certified_by, slice?.certification_details],
);
const faveStarProps = useMemo(
() => ({
itemId: slice?.slice_id ?? 0,
fetchFaveStar: actions.fetchFaveStar,
saveFaveStar: actions.saveFaveStar,
isStarred,
showTooltip: true,
}),
[actions.fetchFaveStar, actions.saveFaveStar, isStarred, slice?.slice_id],
);
const titlePanelAdditionalItems = useMemo(
() => (
<div css={additionalItemsStyles}>
{sliceFormData ? (
<AlteredSliceTag
className="altered"
diffs={formDiffs}
origFormData={originalFormData as QueryFormData}
currentFormData={currentFormData as QueryFormData}
/>
) : null}
{formData && isMatrixifyEnabled(formData as MatrixifyFormData) && (
<Tag name="Matrixified" color="purple" />
)}
{metadataBar}
</div>
),
[
currentFormData,
formData,
formDiffs,
metadataBar,
originalFormData,
sliceFormData,
],
);
const rightPanelAdditionalItems = useMemo(
() => (
<Tooltip
title={
saveDisabled ? t('Add required control values to save chart') : null
}
>
{/* needed to wrap button in a div - antd tooltip doesn't work with disabled button */}
<div>
<Button
buttonStyle="secondary"
onClick={showModal}
disabled={saveDisabled}
data-test="query-save-button"
css={saveButtonStyles}
icon={<Icons.SaveOutlined />}
>
{t('Save')}
</Button>
</div>
</Tooltip>
),
[saveDisabled, showModal],
);
const menuDropdownProps = useMemo(
() => ({
open: isDropdownVisible,
onOpenChange: setIsDropdownVisible,
}),
[isDropdownVisible, setIsDropdownVisible],
);
return (
<>
<PageHeaderWithActions
editableTitleProps={{
title: sliceName ?? '',
canEdit:
!slice ||
canOverwrite ||
(user?.userId !== undefined &&
(slice?.owners || []).includes(user.userId)),
onSave: actions.updateChartTitle,
placeholder: t('Add the name of the chart'),
label: t('Chart title'),
}}
editableTitleProps={editableTitleProps}
showTitlePanelItems={!!slice}
certificatiedBadgeProps={{
certifiedBy: slice?.certified_by,
details: slice?.certification_details,
}}
certificatiedBadgeProps={certificatiedBadgeProps}
showFaveStar={!!user?.userId && slice?.slice_id !== undefined}
faveStarProps={{
itemId: slice?.slice_id ?? 0,
fetchFaveStar: actions.fetchFaveStar,
saveFaveStar: actions.saveFaveStar,
isStarred,
showTooltip: true,
}}
titlePanelAdditionalItems={
<div css={additionalItemsStyles}>
{sliceFormData ? (
<AlteredSliceTag
className="altered"
diffs={formDiffs}
origFormData={originalFormData as QueryFormData}
currentFormData={currentFormData as QueryFormData}
/>
) : null}
{formData && isMatrixifyEnabled(formData as MatrixifyFormData) && (
<Tag name="Matrixified" color="purple" />
)}
{metadataBar}
</div>
}
rightPanelAdditionalItems={
<Tooltip
title={
saveDisabled
? t('Add required control values to save chart')
: null
}
>
{/* needed to wrap button in a div - antd tooltip doesn't work with disabled button */}
<div>
<Button
buttonStyle="secondary"
onClick={showModal}
disabled={saveDisabled}
data-test="query-save-button"
css={saveButtonStyles}
icon={<Icons.SaveOutlined />}
>
{t('Save')}
</Button>
</div>
</Tooltip>
}
faveStarProps={faveStarProps}
titlePanelAdditionalItems={titlePanelAdditionalItems}
rightPanelAdditionalItems={rightPanelAdditionalItems}
additionalActionsMenu={menu}
menuDropdownProps={{
open: isDropdownVisible,
onOpenChange: setIsDropdownVisible,
}}
menuDropdownProps={menuDropdownProps}
/>
{isPropertiesModalOpen && (
<PropertiesModal
@@ -398,4 +433,4 @@ export const ExploreChartHeader: FC<ExploreChartHeaderProps> = ({
);
};
export default ExploreChartHeader;
export default memo(ExploreChartHeader);

View File

@@ -58,7 +58,9 @@ 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: {

View File

@@ -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(),

View File

@@ -413,7 +413,10 @@ function ExploreViewContainer(props: ExploreViewContainerProps) {
);
const addHistory = useCallback(
async ({ isReplace = false, title } = {}) => {
async ({
isReplace = false,
title,
}: { isReplace?: boolean; title?: string } = {}) => {
const formData = props.dashboardId
? {
...props.form_data,

View File

@@ -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<string>;
children?: ReactNode;
};
const StashFormDataContainer: FC<Props> = ({

View File

@@ -16,17 +16,26 @@
* specific language governing permissions and limitations
* under the License.
*/
import React, { Component } from 'react';
import React, { useCallback, useMemo } from 'react';
import { IconTooltip, List } from '@superset-ui/core/components';
import { nanoid } from 'nanoid';
import { t } from '@apache-superset/core/translation';
import { withTheme, type SupersetTheme } from '@apache-superset/core/theme';
import { useTheme, type SupersetTheme } from '@apache-superset/core/theme';
import {
SortableContainer,
SortableHandle,
SortableElement,
DndContext,
closestCenter,
useSensor,
useSensors,
PointerSensor,
type DragEndEvent,
} from '@dnd-kit/core';
import {
SortableContext,
verticalListSortingStrategy,
useSortable,
arrayMove,
} from 'react-sortable-hoc';
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { Icons } from '@superset-ui/core/components/Icons';
import {
HeaderContainer,
@@ -54,161 +63,243 @@ interface CollectionControlProps {
isFloat?: boolean;
isInt?: boolean;
controlName: string;
theme: SupersetTheme;
}
const defaultProps: Partial<CollectionControlProps> = {
label: null,
description: null,
onChange: () => {},
placeholder: t('Empty collection'),
itemGenerator: () => ({ key: nanoid(11) }),
keyAccessor: (o: CollectionItem) => o.key ?? '',
value: [],
addTooltip: t('Add an item'),
};
const SortableListItem = SortableElement(CustomListItem);
const SortableList = SortableContainer(List);
const SortableDragger = SortableHandle(() => (
<Icons.MenuOutlined
role="img"
aria-label={t('Drag to reorder')}
className="text-primary"
style={{ cursor: 'ns-resize' }}
/>
));
function DragHandle() {
return (
<Icons.MenuOutlined
role="img"
aria-label={t('Drag to reorder')}
className="text-primary"
style={{ cursor: 'ns-resize' }}
/>
);
}
class CollectionControl extends Component<CollectionControlProps> {
static defaultProps = defaultProps;
interface SortableItemProps {
id: string;
index: number;
item: CollectionItem;
controlProps: Omit<CollectionControlProps, 'label'>;
onChangeItem: (index: number, value: CollectionItem) => void;
onRemoveItem: (index: number) => void;
}
constructor(props: CollectionControlProps) {
super(props);
this.onAdd = this.onAdd.bind(this);
}
function SortableItem({
id,
index,
item,
controlProps,
onChangeItem,
onRemoveItem,
}: SortableItemProps) {
const { attributes, listeners, setNodeRef, transform, transition } =
useSortable({ id });
const style = {
transform: CSS.Transform.toString(transform),
transition: transition ?? undefined,
};
const Control = (controlMap as Record<string, React.ComponentType<any>>)[
controlProps.controlName
];
onChange(i: number, value: CollectionItem) {
const currentValue = this.props.value ?? [];
const newValue = [...currentValue];
newValue[i] = { ...currentValue[i], ...value };
this.props.onChange?.(newValue);
}
onAdd() {
const currentValue = this.props.value ?? [];
const newItem = this.props.itemGenerator?.();
// Cast needed: original JS allowed undefined items from itemGenerator
this.props.onChange?.(
currentValue.concat([newItem] as unknown as CollectionItem[]),
);
}
onSortEnd({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }) {
const currentValue = this.props.value ?? [];
this.props.onChange?.(arrayMove(currentValue, oldIndex, newIndex));
}
removeItem(i: number) {
const currentValue = this.props.value ?? [];
this.props.onChange?.(currentValue.filter((o, ix) => i !== ix));
}
renderList() {
const currentValue = this.props.value ?? [];
if (currentValue.length === 0) {
return <div className="text-muted">{this.props.placeholder}</div>;
}
const Control = (controlMap as Record<string, React.ComponentType<any>>)[
this.props.controlName
];
const keyAccessor =
this.props.keyAccessor ?? ((o: CollectionItem) => o.key ?? '');
return (
<SortableList
useDragHandle
lockAxis="y"
onSortEnd={this.onSortEnd.bind(this)}
bordered
return (
<CustomListItem
ref={setNodeRef}
style={style}
selectable={false}
className="clearfix"
css={(theme: SupersetTheme) => ({
alignItems: 'center',
justifyContent: 'flex-start',
display: 'flex',
paddingInline: theme.sizeUnit * 6,
})}
>
<span {...attributes} {...listeners}>
<DragHandle />
</span>
<div
css={(theme: SupersetTheme) => ({
borderRadius: theme.borderRadius,
flex: 1,
marginLeft: theme.sizeUnit * 2,
marginRight: theme.sizeUnit * 2,
})}
>
{currentValue.map((o: CollectionItem, i: number) => {
// label relevant only for header, not here
const { label, theme, ...commonProps } = this.props;
return (
<SortableListItem
selectable={false}
className="clearfix"
css={(theme: SupersetTheme) => ({
alignItems: 'center',
justifyContent: 'flex-start',
display: 'flex',
paddingInline: theme.sizeUnit * 6,
})}
key={keyAccessor(o)}
index={i}
>
<SortableDragger />
<div
css={(theme: SupersetTheme) => ({
flex: 1,
marginLeft: theme.sizeUnit * 2,
marginRight: theme.sizeUnit * 2,
})}
>
<Control
{...commonProps}
{...o}
onChange={this.onChange.bind(this, i)}
/>
</div>
<IconTooltip
className="pointer"
placement="right"
onClick={this.removeItem.bind(this, i)}
tooltip={t('Remove item')}
mouseEnterDelay={0}
mouseLeaveDelay={0}
css={(theme: SupersetTheme) => ({
padding: 0,
minWidth: 'auto',
height: 'auto',
lineHeight: 1,
cursor: 'pointer',
'& svg path': {
fill: theme.colorIcon,
transition: `fill ${theme.motionDurationMid} ease-out`,
},
'&:hover svg path': {
fill: theme.colorError,
},
})}
>
<Icons.CloseOutlined iconSize="s" />
</IconTooltip>
</SortableListItem>
);
})}
</SortableList>
);
}
render() {
return (
<div data-test="CollectionControl" className="CollectionControl">
<HeaderContainer>
<ControlHeader {...this.props} />
<AddIconButton onClick={this.onAdd}>
<Icons.PlusOutlined
iconSize="s"
iconColor={this.props.theme.colorTextLightSolid}
/>
</AddIconButton>
</HeaderContainer>
{this.renderList()}
<Control
{...controlProps}
{...item}
onChange={(value: CollectionItem) => onChangeItem(index, value)}
/>
</div>
);
}
<IconTooltip
className="pointer"
placement="right"
onClick={() => onRemoveItem(index)}
tooltip={t('Remove item')}
mouseEnterDelay={0}
mouseLeaveDelay={0}
css={(theme: SupersetTheme) => ({
padding: 0,
minWidth: 'auto',
height: 'auto',
lineHeight: 1,
cursor: 'pointer',
'& svg path': {
fill: theme.colorIcon,
transition: `fill ${theme.motionDurationMid} ease-out`,
},
'&:hover svg path': {
fill: theme.colorError,
},
})}
>
<Icons.CloseOutlined iconSize="s" />
</IconTooltip>
</CustomListItem>
);
}
export default withTheme(CollectionControl);
const defaultKeyAccessor = (o: CollectionItem) => o.key ?? '';
const defaultItemGenerator = () => ({ key: nanoid(11) });
function CollectionControl({
name,
label = null,
description = null,
placeholder = t('Empty collection'),
addTooltip = t('Add an item'),
itemGenerator = defaultItemGenerator,
keyAccessor = defaultKeyAccessor,
onChange,
value = [],
isFloat,
isInt,
controlName,
}: CollectionControlProps) {
const theme = useTheme();
const sensors = useSensors(
useSensor(PointerSensor, {
activationConstraint: { distance: 5 },
}),
);
const itemIds = useMemo(
() => value.map((item, i) => keyAccessor(item) || String(i)),
[value, keyAccessor],
);
const onAdd = useCallback(() => {
const newItem = itemGenerator();
onChange?.(value.concat([newItem] as unknown as CollectionItem[]));
}, [value, itemGenerator, onChange]);
const onChangeItem = useCallback(
(i: number, itemValue: CollectionItem) => {
const newValue = [...value];
newValue[i] = { ...value[i], ...itemValue };
onChange?.(newValue);
},
[value, onChange],
);
const onRemoveItem = useCallback(
(i: number) => {
onChange?.(value.filter((_, ix) => i !== ix));
},
[value, onChange],
);
const handleDragEnd = useCallback(
(event: DragEndEvent) => {
const { active, over } = event;
if (over && active.id !== over.id) {
const oldIndex = itemIds.indexOf(String(active.id));
const newIndex = itemIds.indexOf(String(over.id));
onChange?.(arrayMove(value, oldIndex, newIndex));
}
},
[value, itemIds, onChange],
);
const controlProps = useMemo(
() => ({
name,
description,
placeholder,
addTooltip,
itemGenerator,
keyAccessor,
onChange,
value,
isFloat,
isInt,
controlName,
}),
[
name,
description,
placeholder,
addTooltip,
itemGenerator,
keyAccessor,
onChange,
value,
isFloat,
isInt,
controlName,
],
);
const renderList = () => {
if (value.length === 0) {
return <div className="text-muted">{placeholder}</div>;
}
return (
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragEnd={handleDragEnd}
>
<SortableContext items={itemIds} strategy={verticalListSortingStrategy}>
<List
bordered
css={(theme: SupersetTheme) => ({
borderRadius: theme.borderRadius,
})}
>
{value.map((item, i) => (
<SortableItem
key={itemIds[i]}
id={itemIds[i]}
index={i}
item={item}
controlProps={controlProps}
onChangeItem={onChangeItem}
onRemoveItem={onRemoveItem}
/>
))}
</List>
</SortableContext>
</DndContext>
);
};
return (
<div data-test="CollectionControl" className="CollectionControl">
<HeaderContainer>
<ControlHeader name={name} label={label} description={description} />
<AddIconButton onClick={onAdd}>
<Icons.PlusOutlined
iconSize="s"
iconColor={theme.colorTextLightSolid}
/>
</AddIconButton>
</HeaderContainer>
{renderList()}
</div>
);
}
export default CollectionControl;

View File

@@ -16,39 +16,48 @@
* 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 { selectable, children, ...rest } = props;
const theme = useTheme();
const css: Record<string, Record<string, Record<string, number> | string>> = {
'&.ant-list-item': {
':first-of-type': {
borderTopLeftRadius: theme.borderRadius,
borderTopRightRadius: theme.borderRadius,
const CustomListItem = forwardRef<HTMLDivElement, CustomListItemProps>(
function CustomListItem(props, ref) {
const { selectable, children, ...rest } = props;
const theme = useTheme();
const css: Record<
string,
Record<string, Record<string, number> | string>
> = {
'&.ant-list-item': {
':first-of-type': {
borderTopLeftRadius: theme.borderRadius,
borderTopRightRadius: theme.borderRadius,
},
':last-of-type': {
borderBottomLeftRadius: theme.borderRadius,
borderBottomRightRadius: theme.borderRadius,
},
},
':last-of-type': {
borderBottomLeftRadius: theme.borderRadius,
borderBottomRightRadius: theme.borderRadius,
},
},
};
if (selectable) {
css['&:hover'] = {
cursor: 'pointer',
backgroundColor: theme.colorFillSecondary,
};
}
return (
<List.Item {...rest} css={css}>
{children}
</List.Item>
);
}
if (selectable) {
css['&:hover'] = {
cursor: 'pointer',
backgroundColor: theme.colorFillSecondary,
};
}
return (
<List.Item ref={ref} {...rest} css={css}>
{children}
</List.Item>
);
},
);
export default CustomListItem;

View File

@@ -203,7 +203,7 @@ const ColumnSelectPopover = ({
);
const onSqlExpressionChange = useCallback(
sqlExpression => {
(sqlExpression: string) => {
setAdhocColumn({ label, sqlExpression, expressionType: 'SQL' });
setSelectedSimpleColumn(undefined);
setSelectedCalculatedColumn(undefined);
@@ -213,7 +213,7 @@ const ColumnSelectPopover = ({
);
const onCalculatedColumnChange = useCallback(
selectedColumnName => {
(selectedColumnName: string) => {
const selectedColumn = calculatedColumns.find(
col => col.column_name === selectedColumnName,
);
@@ -229,7 +229,7 @@ const ColumnSelectPopover = ({
);
const onSimpleColumnChange = useCallback(
selectedColumnName => {
(selectedColumnName: string) => {
const selectedColumn = simpleColumns.find(
col => col.column_name === selectedColumnName,
);
@@ -245,7 +245,7 @@ const ColumnSelectPopover = ({
);
const onSimpleMetricChange = useCallback(
selectedMetricName => {
(selectedMetricName: string) => {
const selectedMetric = availableMetrics.find(
metric => metric.metric_name === selectedMetricName,
);
@@ -261,7 +261,7 @@ const ColumnSelectPopover = ({
);
const onSimpleItemChange = useCallback(
selectedValue => {
(selectedValue: string) => {
const selectedColumn = columnMap[selectedValue];
if (selectedColumn) {
onSimpleColumnChange(selectedValue);
@@ -349,7 +349,7 @@ const ColumnSelectPopover = ({
]);
const onTabChange = useCallback(
tab => {
(tab: string) => {
getCurrentTab(tab);
setSelectedTab(tab);
sqlEditorRef.current?.focus();

View File

@@ -63,7 +63,7 @@ export const DndColumnSelectPopoverTitle = ({
}, []);
const onInputBlur = useCallback(
e => {
(e: React.FocusEvent<HTMLInputElement>) => {
if (e.target.value === '') {
onChange(e);
}

View File

@@ -139,7 +139,7 @@ const DndMetricSelect = (props: any) => {
);
const handleChange = useCallback(
opts => {
(opts: ValueType | ValueType[] | null) => {
// if clear out options
if (opts === null) {
onChange(null);
@@ -150,7 +150,11 @@ const DndMetricSelect = (props: any) => {
const optionValues = transformedOpts
.map(option => {
// pre-defined metric
if (option.metric_name) {
if (
typeof option === 'object' &&
'metric_name' in option &&
option.metric_name
) {
return option.metric_name;
}
return option;
@@ -263,7 +267,7 @@ const DndMetricSelect = (props: any) => {
);
const getSavedMetricOptionsForMetric = useCallback(
index =>
(index: number) =>
getOptionsForSavedMetrics(
props.savedMetrics,
props.value,

View File

@@ -44,7 +44,7 @@ export default function Option({
}: OptionProps) {
const theme = useTheme();
const onClickClose = useCallback(
e => {
(e: React.MouseEvent) => {
e.stopPropagation();
clickClose(index);
},

View File

@@ -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 {

View File

@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { renderHook, cleanup } from '@testing-library/react-hooks';
import { renderHook, cleanup } from '@testing-library/react';
import { TestDataset } from '@superset-ui/chart-controls';
import { useDatePickerInAdhocFilter } from './useDatePickerInAdhocFilter';

View File

@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { renderHook } from '@testing-library/react-hooks';
import { renderHook, waitFor } from '@testing-library/react';
import { NO_TIME_RANGE, fetchTimeRange } from '@superset-ui/core';
import { Operators } from 'src/explore/constants';
import { useGetTimeRangeLabel } from './useGetTimeRangeLabel';
@@ -80,10 +80,12 @@ test('should get actualTimeRange and title', async () => {
clause: Clauses.Where,
});
const { result } = await renderHook(() => useGetTimeRangeLabel(adhocFilter));
expect(result.current).toEqual({
actualTimeRange: 'MOCK TIME',
title: 'Last week',
const { result } = renderHook(() => useGetTimeRangeLabel(adhocFilter));
await waitFor(() => {
expect(result.current).toEqual({
actualTimeRange: 'MOCK TIME',
title: 'Last week',
});
});
});
@@ -98,9 +100,11 @@ test('should get actualTimeRange and title when gets an error', async () => {
clause: Clauses.Where,
});
const { result } = await renderHook(() => useGetTimeRangeLabel(adhocFilter));
expect(result.current).toEqual({
actualTimeRange: 'temporal column (Last week)',
title: 'MOCK ERROR',
const { result } = renderHook(() => useGetTimeRangeLabel(adhocFilter));
await waitFor(() => {
expect(result.current).toEqual({
actualTimeRange: 'temporal column (Last week)',
title: 'MOCK ERROR',
});
});
});

View File

@@ -107,7 +107,7 @@ test('accepts an edited metric from an AdhocMetricEditPopover', async () => {
userEvent.click(metricLabel);
await screen.findByText('aggregate');
selectOption('AVG', 'Select aggregate options');
await selectOption('AVG', 'Select aggregate options');
await screen.findByText('AVG(value)');