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,11 +1,13 @@
import React from 'react';
import { shallow } from 'enzyme';
import { UsersActionBar, Props } from './UsersActionBar';
import { Props, UsersActionBar } from './UsersActionBar';
import { mockToolkitActionCreator } from 'test/core/redux/mocks';
import { setUsersSearchQuery } from './state/reducers';
const setup = (propOverrides?: object) => {
const props: Props = {
searchQuery: '',
setUsersSearchQuery: jest.fn(),
setUsersSearchQuery: mockToolkitActionCreator(setUsersSearchQuery),
onShowInvites: jest.fn(),
pendingInvitesCount: 0,
canInvite: false,

View File

@@ -1,7 +1,7 @@
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import classNames from 'classnames';
import { setUsersSearchQuery } from './state/actions';
import { setUsersSearchQuery } from './state/reducers';
import { getInviteesCount, getUsersSearchQuery } from './state/selectors';
import { FilterInput } from 'app/core/components/FilterInput/FilterInput';

View File

@@ -1,10 +1,12 @@
import React from 'react';
import { shallow } from 'enzyme';
import { UsersListPage, Props } from './UsersListPage';
import { Props, UsersListPage } from './UsersListPage';
import { Invitee, OrgUser } from 'app/types';
import { getMockUser } from './__mocks__/userMocks';
import appEvents from '../../core/app_events';
import { NavModel } from '@grafana/data';
import { mockToolkitActionCreator } from 'test/core/redux/mocks';
import { setUsersSearchQuery } from './state/reducers';
jest.mock('../../core/app_events', () => ({
emit: jest.fn(),
@@ -28,7 +30,7 @@ const setup = (propOverrides?: object) => {
loadUsers: jest.fn(),
updateUser: jest.fn(),
removeUser: jest.fn(),
setUsersSearchQuery: jest.fn(),
setUsersSearchQuery: mockToolkitActionCreator(setUsersSearchQuery),
hasFetched: false,
};

View File

@@ -1,17 +1,18 @@
import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import { connect } from 'react-redux';
import { renderMarkdown } from '@grafana/data';
import { NavModel, renderMarkdown } from '@grafana/data';
import Page from 'app/core/components/Page/Page';
import UsersActionBar from './UsersActionBar';
import UsersTable from './UsersTable';
import InviteesTable from './InviteesTable';
import { Invitee, OrgUser, CoreEvents } from 'app/types';
import { CoreEvents, Invitee, OrgUser } from 'app/types';
import appEvents from 'app/core/app_events';
import { loadUsers, loadInvitees, setUsersSearchQuery, updateUser, removeUser } from './state/actions';
import { loadInvitees, loadUsers, removeUser, updateUser } from './state/actions';
import { getNavModel } from 'app/core/selectors/navModel';
import { getInvitees, getUsers, getUsersSearchQuery } from './state/selectors';
import { NavModel } from '@grafana/data';
import { setUsersSearchQuery } from './state/reducers';
export interface Props {
navModel: NavModel;

View File

@@ -1,47 +1,7 @@
import { ThunkAction } from 'redux-thunk';
import { StoreState } from '../../../types';
import { ThunkResult } from '../../../types';
import { getBackendSrv } from '@grafana/runtime';
import { Invitee, OrgUser } from 'app/types';
export enum ActionTypes {
LoadUsers = 'LOAD_USERS',
LoadInvitees = 'LOAD_INVITEES',
SetUsersSearchQuery = 'SET_USERS_SEARCH_QUERY',
}
export interface LoadUsersAction {
type: ActionTypes.LoadUsers;
payload: OrgUser[];
}
export interface LoadInviteesAction {
type: ActionTypes.LoadInvitees;
payload: Invitee[];
}
export interface SetUsersSearchQueryAction {
type: ActionTypes.SetUsersSearchQuery;
payload: string;
}
const usersLoaded = (users: OrgUser[]): LoadUsersAction => ({
type: ActionTypes.LoadUsers,
payload: users,
});
const inviteesLoaded = (invitees: Invitee[]): LoadInviteesAction => ({
type: ActionTypes.LoadInvitees,
payload: invitees,
});
export const setUsersSearchQuery = (query: string): SetUsersSearchQueryAction => ({
type: ActionTypes.SetUsersSearchQuery,
payload: query,
});
export type Action = LoadUsersAction | SetUsersSearchQueryAction | LoadInviteesAction;
type ThunkResult<R> = ThunkAction<R, StoreState, undefined, Action>;
import { OrgUser } from 'app/types';
import { inviteesLoaded, usersLoaded } from './reducers';
export function loadUsers(): ThunkResult<void> {
return async dispatch => {

View File

@@ -0,0 +1,44 @@
import { reducerTester } from '../../../../test/core/redux/reducerTester';
import { UsersState } from '../../../types';
import { initialState, inviteesLoaded, setUsersSearchQuery, usersLoaded, usersReducer } from './reducers';
import { getMockInvitees, getMockUsers } from '../__mocks__/userMocks';
describe('usersReducer', () => {
describe('when usersLoaded is dispatched', () => {
it('then state should be correct', () => {
reducerTester<UsersState>()
.givenReducer(usersReducer, { ...initialState })
.whenActionIsDispatched(usersLoaded(getMockUsers(1)))
.thenStateShouldEqual({
...initialState,
users: getMockUsers(1),
hasFetched: true,
});
});
});
describe('when inviteesLoaded is dispatched', () => {
it('then state should be correct', () => {
reducerTester<UsersState>()
.givenReducer(usersReducer, { ...initialState })
.whenActionIsDispatched(inviteesLoaded(getMockInvitees(1)))
.thenStateShouldEqual({
...initialState,
invitees: getMockInvitees(1),
hasFetched: true,
});
});
});
describe('when setUsersSearchQuery is dispatched', () => {
it('then state should be correct', () => {
reducerTester<UsersState>()
.givenReducer(usersReducer, { ...initialState })
.whenActionIsDispatched(setUsersSearchQuery('a query'))
.thenStateShouldEqual({
...initialState,
searchQuery: 'a query',
});
});
});
});

View File

@@ -1,5 +1,6 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { Invitee, OrgUser, UsersState } from 'app/types';
import { Action, ActionTypes } from './actions';
import config from 'app/core/config';
export const initialState: UsersState = {
@@ -13,20 +14,25 @@ export const initialState: UsersState = {
hasFetched: false,
};
export const usersReducer = (state = initialState, action: Action): UsersState => {
switch (action.type) {
case ActionTypes.LoadUsers:
const usersSlice = createSlice({
name: 'users',
initialState,
reducers: {
usersLoaded: (state, action: PayloadAction<OrgUser[]>): UsersState => {
return { ...state, hasFetched: true, users: action.payload };
case ActionTypes.LoadInvitees:
},
inviteesLoaded: (state, action: PayloadAction<Invitee[]>): UsersState => {
return { ...state, hasFetched: true, invitees: action.payload };
case ActionTypes.SetUsersSearchQuery:
},
setUsersSearchQuery: (state, action: PayloadAction<string>): UsersState => {
return { ...state, searchQuery: action.payload };
}
},
},
});
return state;
};
export const { inviteesLoaded, setUsersSearchQuery, usersLoaded } = usersSlice.actions;
export const usersReducer = usersSlice.reducer;
export default {
users: usersReducer,