mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
@@ -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,
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
44
public/app/features/users/state/reducers.test.ts
Normal file
44
public/app/features/users/state/reducers.test.ts
Normal 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',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user