diff --git a/superset-frontend/src/dashboard/actions/dashboardState.js b/superset-frontend/src/dashboard/actions/dashboardState.js index d0953e886a..36868b85d5 100644 --- a/superset-frontend/src/dashboard/actions/dashboardState.js +++ b/superset-frontend/src/dashboard/actions/dashboardState.js @@ -275,6 +275,32 @@ export function fetchCharts( }; } +const refreshCharts = (chartList, force, interval, dashboardId, dispatch) => + new Promise(resolve => { + dispatch(fetchCharts(chartList, force, interval, dashboardId)); + resolve(); + }); + +export const ON_REFRESH_SUCCESS = 'ON_REFRESH_SUCCESS'; +export function onRefreshSuccess() { + return { type: ON_REFRESH_SUCCESS }; +} + +export const ON_REFRESH = 'ON_REFRESH'; +export function onRefresh( + chartList = [], + force = false, + interval = 0, + dashboardId, +) { + return dispatch => { + dispatch({ type: ON_REFRESH }); + refreshCharts(chartList, force, interval, dashboardId, dispatch).then(() => + dispatch({ type: ON_REFRESH_SUCCESS }), + ); + }; +} + export const SHOW_BUILDER_PANE = 'SHOW_BUILDER_PANE'; export function showBuilderPane() { return { type: SHOW_BUILDER_PANE }; diff --git a/superset-frontend/src/dashboard/actions/hydrate.js b/superset-frontend/src/dashboard/actions/hydrate.js index 8eebbba238..67ce361baa 100644 --- a/superset-frontend/src/dashboard/actions/hydrate.js +++ b/superset-frontend/src/dashboard/actions/hydrate.js @@ -378,6 +378,7 @@ export const hydrateDashboard = (dashboardData, chartData) => ( hasUnsavedChanges: false, maxUndoHistoryExceeded: false, lastModifiedTime: dashboardData.changed_on, + isRefreshing: false, activeTabs: [], }, dashboardLayout, diff --git a/superset-frontend/src/dashboard/components/Header/Header.test.tsx b/superset-frontend/src/dashboard/components/Header/Header.test.tsx index 88d22b8ced..da527571e0 100644 --- a/superset-frontend/src/dashboard/components/Header/Header.test.tsx +++ b/superset-frontend/src/dashboard/components/Header/Header.test.tsx @@ -54,6 +54,7 @@ const createProps = () => ({ onChange: jest.fn(), fetchFaveStar: jest.fn(), fetchCharts: jest.fn(), + onRefresh: jest.fn(), saveFaveStar: jest.fn(), savePublished: jest.fn(), isPublished: false, @@ -301,5 +302,5 @@ test('should refresh the charts', async () => { render(setup(mockedProps)); await openActionsDropdown(); userEvent.click(screen.getByText('Refresh dashboard')); - expect(mockedProps.fetchCharts).toHaveBeenCalledTimes(1); + expect(mockedProps.onRefresh).toHaveBeenCalledTimes(1); }); diff --git a/superset-frontend/src/dashboard/components/Header/index.jsx b/superset-frontend/src/dashboard/components/Header/index.jsx index 79411fad11..39e47511c5 100644 --- a/superset-frontend/src/dashboard/components/Header/index.jsx +++ b/superset-frontend/src/dashboard/components/Header/index.jsx @@ -82,6 +82,7 @@ const propTypes = { lastModifiedTime: PropTypes.number.isRequired, // redux + onRefresh: PropTypes.func.isRequired, onUndo: PropTypes.func.isRequired, onRedo: PropTypes.func.isRequired, undoLength: PropTypes.number.isRequired, @@ -216,8 +217,7 @@ class Header extends React.PureComponent { interval: 0, chartCount: chartList.length, }); - - return this.props.fetchCharts( + return this.props.onRefresh( chartList, true, 0, diff --git a/superset-frontend/src/dashboard/components/Header/types.ts b/superset-frontend/src/dashboard/components/Header/types.ts index 3c8e8cfd92..22624110fc 100644 --- a/superset-frontend/src/dashboard/components/Header/types.ts +++ b/superset-frontend/src/dashboard/components/Header/types.ts @@ -87,6 +87,7 @@ export interface HeaderProps { lastModifiedTime: number; onUndo: () => void; onRedo: () => void; + onRefresh: () => void; undoLength: number; redoLength: number; setMaxUndoHistoryExceeded: () => void; diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx index eb9ace7e9d..0ce0ad0c72 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx @@ -28,7 +28,7 @@ import { JsonObject, getChartMetadataRegistry, } from '@superset-ui/core'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { areObjectsEqual } from 'src/reduxUtils'; import { getChartDataRequest } from 'src/chart/chartAction'; import Loading from 'src/components/Loading'; @@ -36,6 +36,7 @@ import BasicErrorAlert from 'src/components/ErrorMessage/BasicErrorAlert'; import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags'; import { waitForAsyncData } from 'src/middleware/asyncEvent'; import { ClientErrorObject } from 'src/utils/getClientErrorObject'; +import { RootState } from 'src/dashboard/types'; import { dispatchFocusAction } from './utils'; import { FilterProps } from './types'; import { getFormData } from '../../utils'; @@ -62,6 +63,9 @@ const FilterValue: React.FC = ({ const { id, targets, filterType, adhoc_filters, time_range } = filter; const metadata = getChartMetadataRegistry().get(filterType); const cascadingFilters = useCascadingFilters(id, dataMaskSelected); + const isDashboardRefreshing = useSelector( + state => state.dashboardState.isRefreshing, + ); const [state, setState] = useState([]); const [error, setError] = useState(''); const [formData, setFormData] = useState>({ @@ -78,7 +82,7 @@ const FilterValue: React.FC = ({ const { name: groupby } = column; const hasDataSource = !!datasetId; const [isLoading, setIsLoading] = useState(hasDataSource); - const [isRefreshing, setIsRefreshing] = useState(true); + const [isRefreshing, setIsRefreshing] = useState(false); const dispatch = useDispatch(); useEffect(() => { @@ -102,8 +106,10 @@ const FilterValue: React.FC = ({ }); const filterOwnState = filter.dataMask?.ownState || {}; if ( - !areObjectsEqual(formData, newFormData) || - !areObjectsEqual(ownState, filterOwnState) + !isRefreshing && + (!areObjectsEqual(formData, newFormData) || + !areObjectsEqual(ownState, filterOwnState) || + isDashboardRefreshing) ) { setFormData(newFormData); setOwnState(filterOwnState); @@ -165,6 +171,8 @@ const FilterValue: React.FC = ({ groupby, JSON.stringify(filter), hasDataSource, + isRefreshing, + isDashboardRefreshing, ]); useEffect(() => { diff --git a/superset-frontend/src/dashboard/containers/DashboardHeader.jsx b/superset-frontend/src/dashboard/containers/DashboardHeader.jsx index f54f0f03c4..f8862b9e74 100644 --- a/superset-frontend/src/dashboard/containers/DashboardHeader.jsx +++ b/superset-frontend/src/dashboard/containers/DashboardHeader.jsx @@ -39,6 +39,7 @@ import { setMaxUndoHistoryExceeded, maxUndoHistoryToast, setRefreshFrequency, + onRefresh, } from '../actions/dashboardState'; import { @@ -120,6 +121,7 @@ function mapDispatchToProps(dispatch) { maxUndoHistoryToast, logEvent, setRefreshFrequency, + onRefresh, dashboardInfoChanged, dashboardTitleChanged, updateDataMask, diff --git a/superset-frontend/src/dashboard/reducers/dashboardState.js b/superset-frontend/src/dashboard/reducers/dashboardState.js index 55455dd1c2..3c3963abd4 100644 --- a/superset-frontend/src/dashboard/reducers/dashboardState.js +++ b/superset-frontend/src/dashboard/reducers/dashboardState.js @@ -32,6 +32,8 @@ import { TOGGLE_PUBLISHED, UPDATE_CSS, SET_REFRESH_FREQUENCY, + ON_REFRESH, + ON_REFRESH_SUCCESS, SET_DIRECT_PATH, SET_FOCUSED_FILTER_FIELD, UNSET_FOCUSED_FILTER_FIELD, @@ -128,6 +130,18 @@ export default function dashboardStateReducer(state = {}, action) { hasUnsavedChanges: action.isPersistent, }; }, + [ON_REFRESH]() { + return { + ...state, + isRefreshing: true, + }; + }, + [ON_REFRESH_SUCCESS]() { + return { + ...state, + isRefreshing: false, + }; + }, [SET_DIRECT_PATH]() { return { ...state, diff --git a/superset-frontend/src/dashboard/types.ts b/superset-frontend/src/dashboard/types.ts index c2b316b954..ae602aa7fd 100644 --- a/superset-frontend/src/dashboard/types.ts +++ b/superset-frontend/src/dashboard/types.ts @@ -60,6 +60,7 @@ export type DashboardState = { directPathToChild: string[]; activeTabs: ActiveTabs; fullSizeChartId: number | null; + isRefreshing: boolean; }; export type DashboardInfo = { common: {