Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • minerva/frontend
1 result
Show changes
Commits on Source (3)
Showing
with 242 additions and 87 deletions
......@@ -89,7 +89,7 @@ describe('SearchBar - component', () => {
const { store } = renderComponent({
reactions: {
...INITIAL_STORE_STATE_MOCK.reactions,
reactions: { ...INITIAL_STORE_STATE_MOCK.reactions.reactions, data: reactionsFixture },
data: reactionsFixture,
},
});
const input = screen.getByTestId<HTMLInputElement>('search-input');
......@@ -102,7 +102,7 @@ describe('SearchBar - component', () => {
const { reactions } = store.getState();
expect(reactions.reactions.data).toStrictEqual([]);
expect(reactions.data).toStrictEqual([]);
});
it('should open search drawer if it is not open', () => {
const { store } = renderComponent({
......
......@@ -97,16 +97,9 @@ describe('Drawer - component', () => {
const { store } = renderComponent({
reactions: {
reactions: {
data: reactionsFixture,
loading: 'succeeded',
error: { message: '', name: '' },
},
newReactions: {
data: [],
loading: 'succeeded',
error: { message: '', name: '' },
},
data: reactionsFixture,
loading: 'succeeded',
error: { message: '', name: '' },
},
});
......
......@@ -35,16 +35,9 @@ describe('ReactionDrawer - component', () => {
beforeEach(() =>
renderComponent({
reactions: {
reactions: {
data: reactionsFixture,
loading: 'succeeded',
error: { message: '', name: '' },
},
newReactions: {
data: [],
loading: 'succeeded',
error: { message: '', name: '' },
},
data: [],
loading: 'succeeded',
error: { message: '', name: '' },
},
drawer: DRAWER_INITIAL_STATE,
}),
......@@ -77,16 +70,9 @@ describe('ReactionDrawer - component', () => {
beforeEach(() =>
renderComponent({
reactions: {
reactions: {
data: reactionsFixture,
loading: 'succeeded',
error: { message: '', name: '' },
},
newReactions: {
data: [],
loading: 'succeeded',
error: { message: '', name: '' },
},
data: reactionsFixture,
loading: 'succeeded',
error: { message: '', name: '' },
},
drawer: {
...DRAWER_INITIAL_STATE,
......
......@@ -332,7 +332,7 @@ describe('BioEntitiesPinsListItem - component ', () => {
},
reactions: {
...INITIAL_STORE_STATE_MOCK.reactions,
reactions: { ...INITIAL_STORE_STATE_MOCK.reactions.reactions, data: reactionsFixture },
data: reactionsFixture,
},
},
);
......@@ -345,6 +345,6 @@ describe('BioEntitiesPinsListItem - component ', () => {
const state = store.getState();
expect(state.reactions.reactions.data).toStrictEqual([]);
expect(state.reactions.data).toStrictEqual([]);
});
});
......@@ -25,9 +25,9 @@ import CompartmentCircle from '@/components/Map/MapViewer/MapViewerVector/utils/
import { ModelElement } from '@/types/models';
import Glyph from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph';
import CompartmentPathway from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway';
import { getNewReactions } from '@/redux/reactions/reactions.thunks';
import { newReactionsDataSelector } from '@/redux/reactions/reactions.selector';
import Reaction from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction';
import { newReactionsDataSelector } from '@/redux/newReactions/newReactions.selectors';
import { getNewReactions } from '@/redux/newReactions/newReactions.thunks';
export const useOlMapReactionsLayer = ({
mapInstance,
......
......@@ -45,6 +45,8 @@ export default function getPolygonCoords({
return [[...lastPoint]];
}
const { p1, p2, p3 } = getCurveCoords({ x, y, point, height, width, pointToProjection });
return getBezierCurve({ p0: lastPoint, p1, p2, p3, numPoints: 20 });
const p0 = lastPoint;
lastPoint = p3;
return getBezierCurve({ p0, p1, p2, p3, numPoints: 20 });
});
}
import { DEFAULT_ERROR } from '@/constants/errors';
import { NewReactionsState } from '@/redux/newReactions/newReactions.types';
export const NEW_REACTIONS_INITIAL_STATE: NewReactionsState = {
data: [],
loading: 'idle',
error: DEFAULT_ERROR,
};
export const NEW_REACTIONS_FETCHING_ERROR_PREFIX = 'Failed to fetch new reactions';
import { DEFAULT_ERROR } from '@/constants/errors';
import { NewReactionsState } from '@/redux/newReactions/newReactions.types';
export const NEW_REACTIONS_INITIAL_STATE_MOCK: NewReactionsState = {
data: [],
loading: 'idle',
error: DEFAULT_ERROR,
};
/* eslint-disable no-magic-numbers */
import { apiPath } from '@/redux/apiPath';
import {
ToolkitStoreWithSingleSlice,
createStoreInstanceUsingSliceReducer,
} from '@/utils/createStoreInstanceUsingSliceReducer';
import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse';
import { HttpStatusCode } from 'axios';
import { unwrapResult } from '@reduxjs/toolkit';
import newReactionsReducer from '@/redux/newReactions/newReactions.slice';
import { NewReactionsState } from '@/redux/newReactions/newReactions.types';
import { NEW_REACTIONS_INITIAL_STATE_MOCK } from '@/redux/newReactions/newReactions.mock';
import { getNewReactions } from '@/redux/newReactions/newReactions.thunks';
import { newReactionsFixture } from '@/models/fixtures/newReactionsFixture';
const mockedAxiosClient = mockNetworkNewAPIResponse();
const INITIAL_STATE: NewReactionsState = NEW_REACTIONS_INITIAL_STATE_MOCK;
describe('newReactions reducer', () => {
let store = {} as ToolkitStoreWithSingleSlice<NewReactionsState>;
beforeEach(() => {
store = createStoreInstanceUsingSliceReducer('newReactions', newReactionsReducer);
});
it('should match initial state', () => {
const action = { type: 'unknown' };
expect(newReactionsReducer(undefined, action)).toEqual(INITIAL_STATE);
});
it('should update store after successful getNewReactions query', async () => {
mockedAxiosClient
.onGet(apiPath.getNewReactions(1))
.reply(HttpStatusCode.Ok, newReactionsFixture);
const { type } = await store.dispatch(getNewReactions(1));
const { data, loading, error } = store.getState().newReactions;
expect(type).toBe('newReactions/getNewReactions/fulfilled');
expect(loading).toEqual('succeeded');
expect(error).toEqual({ message: '', name: '' });
expect(data).toEqual(newReactionsFixture.content);
});
it('should update store after failed getNewReactions query', async () => {
mockedAxiosClient.onGet(apiPath.getNewReactions(1)).reply(HttpStatusCode.NotFound, []);
const action = await store.dispatch(getNewReactions(1));
const { data, loading, error } = store.getState().newReactions;
expect(action.type).toBe('newReactions/getNewReactions/rejected');
expect(() => unwrapResult(action)).toThrow(
"Failed to fetch new reactions: The page you're looking for doesn't exist. Please verify the URL and try again.",
);
expect(loading).toEqual('failed');
expect(error).toEqual({ message: '', name: '' });
expect(data).toEqual([]);
});
it('should update store on loading getNewReactions query', async () => {
mockedAxiosClient
.onGet(apiPath.getNewReactions(1))
.reply(HttpStatusCode.Ok, newReactionsFixture);
const newReactionsPromise = store.dispatch(getNewReactions(1));
const { data, loading } = store.getState().newReactions;
expect(data).toEqual([]);
expect(loading).toEqual('pending');
newReactionsPromise.then(() => {
const { data: dataPromiseFulfilled, loading: promiseFulfilled } =
store.getState().newReactions;
expect(dataPromiseFulfilled).toEqual(newReactionsFixture.content);
expect(promiseFulfilled).toEqual('succeeded');
});
});
});
import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
import { getNewReactions } from '@/redux/newReactions/newReactions.thunks';
import { NewReactionsState } from '@/redux/newReactions/newReactions.types';
export const getNewReactionsReducer = (
builder: ActionReducerMapBuilder<NewReactionsState>,
): void => {
builder.addCase(getNewReactions.pending, state => {
state.loading = 'pending';
});
builder.addCase(getNewReactions.fulfilled, (state, action) => {
state.data = action.payload || [];
state.loading = 'succeeded';
});
builder.addCase(getNewReactions.rejected, state => {
state.loading = 'failed';
});
};
import { createSelector } from '@reduxjs/toolkit';
import { rootSelector } from '../root/root.selectors';
export const newReactionsSelector = createSelector(rootSelector, state => state.newReactions);
export const newReactionsDataSelector = createSelector(
newReactionsSelector,
reactions => reactions.data || [],
);
import { createSlice } from '@reduxjs/toolkit';
import { NEW_REACTIONS_INITIAL_STATE } from '@/redux/newReactions/newReactions.constants';
import { getNewReactionsReducer } from '@/redux/newReactions/newReactions.reducers';
export const newReactionsSlice = createSlice({
name: 'reactions',
initialState: NEW_REACTIONS_INITIAL_STATE,
reducers: {},
extraReducers: builder => {
getNewReactionsReducer(builder);
},
});
export default newReactionsSlice.reducer;
/* eslint-disable no-magic-numbers */
import { apiPath } from '@/redux/apiPath';
import {
ToolkitStoreWithSingleSlice,
createStoreInstanceUsingSliceReducer,
} from '@/utils/createStoreInstanceUsingSliceReducer';
import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse';
import { HttpStatusCode } from 'axios';
import newReactionsReducer from '@/redux/newReactions/newReactions.slice';
import { NewReactionsState } from '@/redux/newReactions/newReactions.types';
import { newReactionsFixture } from '@/models/fixtures/newReactionsFixture';
import { getNewReactions } from '@/redux/newReactions/newReactions.thunks';
const mockedAxiosClient = mockNetworkNewAPIResponse();
describe('newReactions thunks', () => {
let store = {} as ToolkitStoreWithSingleSlice<NewReactionsState>;
beforeEach(() => {
store = createStoreInstanceUsingSliceReducer('newReactions', newReactionsReducer);
});
describe('getReactions', () => {
it('should return data when data response from API is valid', async () => {
mockedAxiosClient
.onGet(apiPath.getNewReactions(1))
.reply(HttpStatusCode.Ok, newReactionsFixture);
const { payload } = await store.dispatch(getNewReactions(1));
expect(payload).toEqual(newReactionsFixture.content);
});
it('should return undefined when data response from API is not valid ', async () => {
mockedAxiosClient
.onGet(apiPath.getNewReactions(1))
.reply(HttpStatusCode.Ok, { randomProperty: 'randomValue' });
const { payload } = await store.dispatch(getNewReactions(1));
expect(payload).toEqual(undefined);
});
});
});
import { apiPath } from '@/redux/apiPath';
import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
import { NewReaction, NewReactions } from '@/types/models';
import { ThunkConfig } from '@/types/store';
import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
import { createAsyncThunk } from '@reduxjs/toolkit';
import { getError } from '@/utils/error-report/getError';
import { newReactionSchema } from '@/models/newReactionSchema';
import { pageableSchema } from '@/models/pageableSchema';
import { NEW_REACTIONS_FETCHING_ERROR_PREFIX } from '@/redux/newReactions/newReactions.constants';
export const getNewReactions = createAsyncThunk<
Array<NewReaction> | undefined,
number,
ThunkConfig
>('newReactions/getNewReactions', async (modelId: number) => {
try {
const { data } = await axiosInstanceNewAPI.get<NewReactions>(apiPath.getNewReactions(modelId));
const isDataValid = validateDataUsingZodSchema(data, pageableSchema(newReactionSchema));
return isDataValid ? data.content : undefined;
} catch (error) {
return Promise.reject(getError({ error, prefix: NEW_REACTIONS_FETCHING_ERROR_PREFIX }));
}
});
import { FetchDataState } from '@/types/fetchDataState';
import { NewReaction } from '@/types/models';
export type NewReactionsState = FetchDataState<NewReaction[]>;
import { DEFAULT_ERROR } from '@/constants/errors';
import { ReactionsState } from './reactions.types';
export const REACTIONS_INITIAL_STATE: ReactionsState = {
reactions: {
data: [],
loading: 'idle',
error: DEFAULT_ERROR,
},
newReactions: { data: [], loading: 'idle', error: DEFAULT_ERROR },
data: [],
loading: 'idle',
error: { name: '', message: '' },
};
export const REACTIONS_FETCHING_ERROR_PREFIX = 'Failed to fetch reactions';
export const NEW_REACTIONS_FETCHING_ERROR_PREFIX = 'Failed to fetch new reactions';
......@@ -2,10 +2,7 @@ import { DEFAULT_ERROR } from '@/constants/errors';
import { ReactionsState } from './reactions.types';
export const REACTIONS_STATE_INITIAL_MOCK: ReactionsState = {
reactions: {
data: [],
loading: 'idle',
error: DEFAULT_ERROR,
},
newReactions: { data: [], loading: 'idle', error: DEFAULT_ERROR },
data: [],
loading: 'idle',
error: DEFAULT_ERROR,
};
import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
import { REACTIONS_INITIAL_STATE } from './reactions.constants';
import { getNewReactions, getReactionsByIds } from './reactions.thunks';
import { getReactionsByIds } from './reactions.thunks';
import { ReactionsState } from './reactions.types';
export const getReactionsReducer = (builder: ActionReducerMapBuilder<ReactionsState>): void => {
builder.addCase(getReactionsByIds.pending, state => {
state.reactions.loading = 'pending';
state.loading = 'pending';
});
builder.addCase(getReactionsByIds.fulfilled, (state, action) => {
const { payload } = action;
if (!payload) return;
state.reactions.data = payload.shouldConcat
? [...(state.reactions.data || []), ...payload.data]
: payload.data;
state.reactions.loading = 'succeeded';
const newData = payload.shouldConcat ? [...(state.data || []), ...payload.data] : payload.data;
state.data = newData;
state.loading = 'succeeded';
});
builder.addCase(getReactionsByIds.rejected, state => {
state.reactions.loading = 'failed';
state.loading = 'failed';
// TODO: error management to be discussed in the team
});
};
export const getNewReactionsReducer = (builder: ActionReducerMapBuilder<ReactionsState>): void => {
builder.addCase(getNewReactions.pending, state => {
state.newReactions.loading = 'pending';
});
builder.addCase(getNewReactions.fulfilled, (state, action) => {
state.newReactions.data = action.payload || [];
state.newReactions.loading = 'succeeded';
});
builder.addCase(getNewReactions.rejected, state => {
state.newReactions.loading = 'failed';
});
};
export const resetReactionsDataReducer = (state: ReactionsState): void => {
state.reactions.data = REACTIONS_INITIAL_STATE.reactions.data;
state.reactions.error = REACTIONS_INITIAL_STATE.reactions.error;
state.reactions.loading = REACTIONS_INITIAL_STATE.reactions.loading;
state.data = REACTIONS_INITIAL_STATE.data;
state.error = REACTIONS_INITIAL_STATE.error;
state.loading = REACTIONS_INITIAL_STATE.loading;
};
......@@ -11,12 +11,7 @@ export const reactionsSelector = createSelector(rootSelector, state => state.rea
export const reactionsDataSelector = createSelector(
reactionsSelector,
reactions => reactions.reactions.data || [],
);
export const newReactionsDataSelector = createSelector(
reactionsSelector,
reactions => reactions.newReactions.data || [],
reactions => reactions?.data || [],
);
export const allReactionsSelectorOfCurrentMap = createSelector(
......
import { createSlice } from '@reduxjs/toolkit';
import { REACTIONS_INITIAL_STATE } from './reactions.constants';
import {
getNewReactionsReducer,
getReactionsReducer,
resetReactionsDataReducer,
} from './reactions.reducers';
import { getReactionsReducer, resetReactionsDataReducer } from './reactions.reducers';
export const reactionsSlice = createSlice({
name: 'reactions',
......@@ -14,7 +10,6 @@ export const reactionsSlice = createSlice({
},
extraReducers: builder => {
getReactionsReducer(builder);
getNewReactionsReducer(builder);
},
});
......