Chore: Migrates reducers and actions to Redux Toolkit (#21287)

* Refactor: Adds Redux Toolkit package

* Refactor: Uses configureStore from Redux Toolkit

* Refactor: Migrates applicationReducer

* Refactor: Migrates appNotificationsReducer

* Refactor: Migrates locationReducer

* Refactor: Migrates navModelReducer

* Refactor: Migrates teamsReducer and teamReducer

* Refactor: Migrates cleanUpAction

* Refactor: Migrates alertRulesReducer

* Refactor: Cleans up recursiveCleanState

* Refactor: Switched to Angular compatible reducers

* Refactor: Migrates folderReducer

* Refactor: Migrates dashboardReducer

* Migrates panelEditorReducer

* Refactor: Migrates dataSourcesReducer

* Refactor: Migrates usersReducer

* Refactor: Migrates organizationReducer

* Refactor: Migrates pluginsReducer

* Refactor: Migrates ldapReducer and ldapUserReducer

* Refactor: Migrates apiKeysReducer

* Refactor: Migrates exploreReducer and itemReducer

* Refactor: Removes actionCreatorFactory and reducerFactory

* Refactor: Moves mocks to test section

* Docs: Removes sections about home grown framework

* Update contribute/style-guides/redux.md

Co-Authored-By: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>

* Refactor: Cleans up some code

* Refactor: Adds state typings

* Refactor: Cleans up typings

* Refactor: Adds comment about ImmerJs autoFreeze

Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
This commit is contained in:
Hugo Häggmark
2020-01-13 08:03:22 +01:00
committed by GitHub
parent b0515f46cc
commit 4f0fa776be
97 changed files with 2392 additions and 2305 deletions

View File

@@ -1,9 +1,11 @@
import React from 'react';
import { shallow } from 'enzyme';
import { Props, ApiKeysPage } from './ApiKeysPage';
import { ApiKeysPage, Props } from './ApiKeysPage';
import { ApiKey } from 'app/types';
import { getMultipleMockKeys, getMockKey } from './__mocks__/apiKeysMock';
import { getMockKey, getMultipleMockKeys } from './__mocks__/apiKeysMock';
import { NavModel } from '@grafana/data';
import { setSearchQuery } from './state/reducers';
import { mockToolkitActionCreator } from '../../../test/core/redux/mocks';
const setup = (propOverrides?: object) => {
const props: Props = {
@@ -20,7 +22,7 @@ const setup = (propOverrides?: object) => {
hasFetched: false,
loadApiKeys: jest.fn(),
deleteApiKey: jest.fn(),
setSearchQuery: jest.fn(),
setSearchQuery: mockToolkitActionCreator(setSearchQuery),
addApiKey: jest.fn(),
apiKeysCount: 0,
includeExpired: false,

View File

@@ -2,25 +2,24 @@ import React, { PureComponent } from 'react';
import ReactDOMServer from 'react-dom/server';
import { connect } from 'react-redux';
import { hot } from 'react-hot-loader';
import { ApiKey, NewApiKey, OrgRole } from 'app/types';
// Utils
import { ApiKey, CoreEvents, NewApiKey, OrgRole } from 'app/types';
import { getNavModel } from 'app/core/selectors/navModel';
import { getApiKeys, getApiKeysCount } from './state/selectors';
import { loadApiKeys, deleteApiKey, setSearchQuery, addApiKey } from './state/actions';
import { addApiKey, deleteApiKey, loadApiKeys } from './state/actions';
import Page from 'app/core/components/Page/Page';
import { SlideDown } from 'app/core/components/Animations/SlideDown';
import ApiKeysAddedModal from './ApiKeysAddedModal';
import config from 'app/core/config';
import appEvents from 'app/core/app_events';
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
import { EventsWithValidation, FormLabel, Input, Switch, ValidationEvents, DeleteButton } from '@grafana/ui';
import { NavModel, dateTime, isDateTime } from '@grafana/data';
import { DeleteButton, EventsWithValidation, FormLabel, Input, Switch, ValidationEvents } from '@grafana/ui';
import { dateTime, isDateTime, NavModel } from '@grafana/data';
import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
import { store } from 'app/store/store';
import kbn from 'app/core/utils/kbn';
// Utils
import { CoreEvents } from 'app/types';
import { getTimeZone } from 'app/features/profile/state/selectors';
import { setSearchQuery } from './state/reducers';
const timeRangeValidationEvents: ValidationEvents = {
[EventsWithValidation.onBlur]: [

View File

@@ -1,30 +1,6 @@
import { ThunkAction } from 'redux-thunk';
import { getBackendSrv } from 'app/core/services/backend_srv';
import { StoreState, ApiKey } from 'app/types';
export enum ActionTypes {
LoadApiKeys = 'LOAD_API_KEYS',
SetApiKeysSearchQuery = 'SET_API_KEYS_SEARCH_QUERY',
}
export interface LoadApiKeysAction {
type: ActionTypes.LoadApiKeys;
payload: ApiKey[];
}
export interface SetSearchQueryAction {
type: ActionTypes.SetApiKeysSearchQuery;
payload: string;
}
export type Action = LoadApiKeysAction | SetSearchQueryAction;
type ThunkResult<R> = ThunkAction<R, StoreState, undefined, Action>;
const apiKeysLoaded = (apiKeys: ApiKey[]): LoadApiKeysAction => ({
type: ActionTypes.LoadApiKeys,
payload: apiKeys,
});
import { getBackendSrv } from 'app/core/services/backend_srv';
import { ApiKey, ThunkResult } from 'app/types';
import { apiKeysLoaded, setSearchQuery } from './reducers';
export function addApiKey(
apiKey: ApiKey,
@@ -53,8 +29,3 @@ export function deleteApiKey(id: number, includeExpired: boolean): ThunkResult<v
.then(dispatch(loadApiKeys(includeExpired)));
};
}
export const setSearchQuery = (searchQuery: string): SetSearchQueryAction => ({
type: ActionTypes.SetApiKeysSearchQuery,
payload: searchQuery,
});

View File

@@ -1,31 +1,27 @@
import { Action, ActionTypes } from './actions';
import { initialApiKeysState, apiKeysReducer } from './reducers';
import { apiKeysLoaded, apiKeysReducer, initialApiKeysState, setSearchQuery } from './reducers';
import { getMultipleMockKeys } from '../__mocks__/apiKeysMock';
import { reducerTester } from '../../../../test/core/redux/reducerTester';
import { ApiKeysState } from '../../../types';
describe('API Keys reducer', () => {
it('should set keys', () => {
const payload = getMultipleMockKeys(4);
const action: Action = {
type: ActionTypes.LoadApiKeys,
payload,
};
const result = apiKeysReducer(initialApiKeysState, action);
expect(result.keys).toEqual(payload);
reducerTester<ApiKeysState>()
.givenReducer(apiKeysReducer, { ...initialApiKeysState })
.whenActionIsDispatched(apiKeysLoaded(getMultipleMockKeys(4)))
.thenStateShouldEqual({
...initialApiKeysState,
keys: getMultipleMockKeys(4),
hasFetched: true,
});
});
it('should set search query', () => {
const payload = 'test query';
const action: Action = {
type: ActionTypes.SetApiKeysSearchQuery,
payload,
};
const result = apiKeysReducer(initialApiKeysState, action);
expect(result.searchQuery).toEqual('test query');
reducerTester<ApiKeysState>()
.givenReducer(apiKeysReducer, { ...initialApiKeysState })
.whenActionIsDispatched(setSearchQuery('test query'))
.thenStateShouldEqual({
...initialApiKeysState,
searchQuery: 'test query',
});
});
});

View File

@@ -1,5 +1,6 @@
import { ApiKeysState } from 'app/types';
import { Action, ActionTypes } from './actions';
import { createSlice } from '@reduxjs/toolkit';
import { ApiKeysState } from 'app/types';
export const initialApiKeysState: ApiKeysState = {
keys: [],
@@ -8,15 +9,22 @@ export const initialApiKeysState: ApiKeysState = {
includeExpired: false,
};
export const apiKeysReducer = (state = initialApiKeysState, action: Action): ApiKeysState => {
switch (action.type) {
case ActionTypes.LoadApiKeys:
const apiKeysSlice = createSlice({
name: 'apiKeys',
initialState: initialApiKeysState,
reducers: {
apiKeysLoaded: (state, action): ApiKeysState => {
return { ...state, hasFetched: true, keys: action.payload };
case ActionTypes.SetApiKeysSearchQuery:
},
setSearchQuery: (state, action): ApiKeysState => {
return { ...state, searchQuery: action.payload };
}
return state;
};
},
},
});
export const { setSearchQuery, apiKeysLoaded } = apiKeysSlice.actions;
export const apiKeysReducer = apiKeysSlice.reducer;
export default {
apiKeys: apiKeysReducer,