mirror of
https://github.com/grafana/grafana.git
synced 2026-09-05 04:40:13 -05:00
Datasource Onboarding: Prevent flickering of onboarding page after first load (#63360)
* Datasource Onboarding: Prevent flickering of onboarding page after first load * add loading state to loadDatasources & refactor * fix test * avoid loading state when loading datasources on add * fix test * add explainer on why fetching datasources is needed
This commit is contained in:
@@ -17,11 +17,10 @@ import { constructDataSourceExploreUrl } from '../utils';
|
||||
import { DataSourcesListHeader } from './DataSourcesListHeader';
|
||||
|
||||
export function DataSourcesList() {
|
||||
useLoadDataSources();
|
||||
const { isLoading } = useLoadDataSources();
|
||||
|
||||
const dataSources = useSelector((state) => getDataSources(state.dataSources));
|
||||
const dataSourcesCount = useSelector(({ dataSources }: StoreState) => getDataSourcesCount(dataSources));
|
||||
const hasFetched = useSelector(({ dataSources }: StoreState) => dataSources.hasFetched);
|
||||
const hasCreateRights = contextSrv.hasPermission(AccessControlAction.DataSourcesCreate);
|
||||
const hasWriteRights = contextSrv.hasPermission(AccessControlAction.DataSourcesWrite);
|
||||
const hasExploreRights = contextSrv.hasPermission(AccessControlAction.DataSourcesExplore);
|
||||
@@ -30,7 +29,7 @@ export function DataSourcesList() {
|
||||
<DataSourcesListView
|
||||
dataSources={dataSources}
|
||||
dataSourcesCount={dataSourcesCount}
|
||||
isLoading={!hasFetched}
|
||||
isLoading={isLoading}
|
||||
hasCreateRights={hasCreateRights}
|
||||
hasWriteRights={hasWriteRights}
|
||||
hasExploreRights={hasExploreRights}
|
||||
|
||||
@@ -23,7 +23,7 @@ export function NewDataSource() {
|
||||
const dispatch = useDispatch();
|
||||
const filteredDataSources = useSelector((s: StoreState) => getFilteredDataSourcePlugins(s.dataSources));
|
||||
const searchQuery = useSelector((s: StoreState) => s.dataSources.dataSourceTypeSearchQuery);
|
||||
const isLoading = useSelector((s: StoreState) => s.dataSources.isLoadingDataSources);
|
||||
const isLoadingDatasourcePlugins = useSelector((s: StoreState) => s.dataSources.isLoadingDataSourcePlugins);
|
||||
const dataSourceCategories = useSelector((s: StoreState) => s.dataSources.categories);
|
||||
const onAddDataSource = useAddDatasource();
|
||||
const onSetSearchQuery = (q: string) => dispatch(setDataSourceTypeSearchQuery(q));
|
||||
@@ -33,7 +33,7 @@ export function NewDataSource() {
|
||||
dataSources={filteredDataSources}
|
||||
dataSourceCategories={dataSourceCategories}
|
||||
searchQuery={searchQuery}
|
||||
isLoading={isLoading}
|
||||
isLoading={isLoadingDatasourcePlugins}
|
||||
onAddDataSource={onAddDataSource}
|
||||
onSetSearchQuery={onSetSearchQuery}
|
||||
/>
|
||||
|
||||
@@ -80,7 +80,7 @@ describe('<EditDataSourcePage>', () => {
|
||||
dataSource: dataSource,
|
||||
dataSourceMeta: dataSourceMeta,
|
||||
layoutMode: LayoutModes.Grid,
|
||||
hasFetched: true,
|
||||
isLoadingDataSources: false,
|
||||
},
|
||||
navIndex: {
|
||||
...navIndex,
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
dataSourceMetaLoaded,
|
||||
dataSourcePluginsLoad,
|
||||
dataSourcePluginsLoaded,
|
||||
dataSourcesLoad,
|
||||
dataSourcesLoaded,
|
||||
initDataSourceSettingsFailed,
|
||||
initDataSourceSettingsSucceeded,
|
||||
@@ -144,8 +145,9 @@ export const testDataSource = (
|
||||
};
|
||||
};
|
||||
|
||||
export function loadDataSources(): ThunkResult<void> {
|
||||
export function loadDataSources(): ThunkResult<Promise<void>> {
|
||||
return async (dispatch) => {
|
||||
dispatch(dataSourcesLoad());
|
||||
const response = await api.getDataSources();
|
||||
dispatch(dataSourcesLoaded(response));
|
||||
};
|
||||
@@ -193,9 +195,16 @@ export function loadDataSourceMeta(dataSource: DataSourceSettings): ThunkResult<
|
||||
};
|
||||
}
|
||||
|
||||
export function addDataSource(plugin: DataSourcePluginMeta, editRoute = DATASOURCES_ROUTES.Edit): ThunkResult<void> {
|
||||
export function addDataSource(
|
||||
plugin: DataSourcePluginMeta,
|
||||
editRoute = DATASOURCES_ROUTES.Edit
|
||||
): ThunkResult<Promise<void>> {
|
||||
return async (dispatch, getStore) => {
|
||||
await dispatch(loadDataSources());
|
||||
// update the list of datasources first.
|
||||
// We later use this list to check whether the name of the datasource
|
||||
// being created is unuque or not and assign a new name to it if needed.
|
||||
const response = await api.getDataSources();
|
||||
dispatch(dataSourcesLoaded(response));
|
||||
|
||||
const dataSources = getStore().dataSources.dataSources;
|
||||
const isFirstDataSource = dataSources.length === 0;
|
||||
|
||||
@@ -51,10 +51,14 @@ export const useTestDataSource = (uid: string) => {
|
||||
|
||||
export const useLoadDataSources = () => {
|
||||
const dispatch = useDispatch();
|
||||
const isLoading = useSelector((state) => state.dataSources.isLoadingDataSources);
|
||||
const dataSources = useSelector((state) => state.dataSources.dataSources);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(loadDataSources());
|
||||
}, [dispatch]);
|
||||
|
||||
return { isLoading, dataSources };
|
||||
};
|
||||
|
||||
export const useLoadDataSource = (uid: string) => {
|
||||
|
||||
@@ -47,7 +47,7 @@ describe('dataSourcesReducer', () => {
|
||||
reducerTester<DataSourcesState>()
|
||||
.givenReducer(dataSourcesReducer, initialState)
|
||||
.whenActionIsDispatched(dataSourcesLoaded(dataSources))
|
||||
.thenStateShouldEqual({ ...initialState, hasFetched: true, dataSources, dataSourcesCount: 1 });
|
||||
.thenStateShouldEqual({ ...initialState, isLoadingDataSources: false, dataSources, dataSourcesCount: 1 });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -89,19 +89,19 @@ describe('dataSourcesReducer', () => {
|
||||
reducerTester<DataSourcesState>()
|
||||
.givenReducer(dataSourcesReducer, state)
|
||||
.whenActionIsDispatched(dataSourcePluginsLoad())
|
||||
.thenStateShouldEqual({ ...initialState, isLoadingDataSources: true });
|
||||
.thenStateShouldEqual({ ...initialState, isLoadingDataSourcePlugins: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe('when dataSourcePluginsLoaded is dispatched', () => {
|
||||
it('then state should be correct', () => {
|
||||
const dataSourceTypes = [mockPlugin()];
|
||||
const state: DataSourcesState = { ...initialState, isLoadingDataSources: true };
|
||||
const state: DataSourcesState = { ...initialState, isLoadingDataSourcePlugins: true };
|
||||
|
||||
reducerTester<DataSourcesState>()
|
||||
.givenReducer(dataSourcesReducer, state)
|
||||
.whenActionIsDispatched(dataSourcePluginsLoaded({ plugins: dataSourceTypes, categories: [] }))
|
||||
.thenStateShouldEqual({ ...initialState, plugins: dataSourceTypes, isLoadingDataSources: false });
|
||||
.thenStateShouldEqual({ ...initialState, plugins: dataSourceTypes, isLoadingDataSourcePlugins: false });
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -17,13 +17,14 @@ export const initialState: DataSourcesState = {
|
||||
searchQuery: '',
|
||||
dataSourcesCount: 0,
|
||||
dataSourceTypeSearchQuery: '',
|
||||
hasFetched: false,
|
||||
isLoadingDataSources: false,
|
||||
isLoadingDataSourcePlugins: false,
|
||||
dataSourceMeta: {} as DataSourcePluginMeta,
|
||||
isSortAscending: true,
|
||||
};
|
||||
|
||||
export const dataSourceLoaded = createAction<DataSourceSettings>('dataSources/dataSourceLoaded');
|
||||
export const dataSourcesLoad = createAction<void>('dataSources/dataSourcesLoad');
|
||||
export const dataSourcesLoaded = createAction<DataSourceSettings[]>('dataSources/dataSourcesLoaded');
|
||||
export const dataSourceMetaLoaded = createAction<DataSourcePluginMeta>('dataSources/dataSourceMetaLoaded');
|
||||
export const dataSourcePluginsLoad = createAction('dataSources/dataSourcePluginsLoad');
|
||||
@@ -43,10 +44,14 @@ export const setIsSortAscending = createAction<boolean>('dataSources/setIsSortAs
|
||||
// the frozen state.
|
||||
// https://github.com/reduxjs/redux-toolkit/issues/242
|
||||
export const dataSourcesReducer = (state: DataSourcesState = initialState, action: AnyAction): DataSourcesState => {
|
||||
if (dataSourcesLoad.match(action)) {
|
||||
return { ...state, isLoadingDataSources: true };
|
||||
}
|
||||
|
||||
if (dataSourcesLoaded.match(action)) {
|
||||
return {
|
||||
...state,
|
||||
hasFetched: true,
|
||||
isLoadingDataSources: false,
|
||||
dataSources: action.payload,
|
||||
dataSourcesCount: action.payload.length,
|
||||
};
|
||||
@@ -65,7 +70,7 @@ export const dataSourcesReducer = (state: DataSourcesState = initialState, actio
|
||||
}
|
||||
|
||||
if (dataSourcePluginsLoad.match(action)) {
|
||||
return { ...state, plugins: [], isLoadingDataSources: true };
|
||||
return { ...state, plugins: [], isLoadingDataSourcePlugins: true };
|
||||
}
|
||||
|
||||
if (dataSourcePluginsLoaded.match(action)) {
|
||||
@@ -73,7 +78,7 @@ export const dataSourcesReducer = (state: DataSourcesState = initialState, actio
|
||||
...state,
|
||||
plugins: action.payload.plugins,
|
||||
categories: action.payload.categories,
|
||||
isLoadingDataSources: false,
|
||||
isLoadingDataSourcePlugins: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user