From 728307df099f23454ab4d605c78d4f63968dcc1e Mon Sep 17 00:00:00 2001 From: Siree Koolen-Wijkstra Date: Fri, 8 Dec 2023 09:09:48 +0100 Subject: [PATCH] [bugfix 5573] Error handling when sorting (#2779) * Showing chevron after answer from backend has returned - Chevron is now shown in column that has either a successful return from the backend, or in the column that was previous ordered. - create selector makeSelectErrorMessage - made prop errorMessage in IncidentOverviewPageContainerComponent - set globalNotification when errorMessage changes and is not undefined. * fixed definition of Type_Global and Variant_Notice. Now it is all in one place again. --- .../__tests__/saga.test.js | 10 ++-- .../containers/IncidentOverviewPage/index.js | 26 ++++++++- .../IncidentOverviewPage/index.test.js | 57 +++++++++++++++++-- src/signals/incident-management/reducer.js | 4 +- src/signals/incident-management/saga.js | 6 +- src/signals/incident-management/selectors.js | 9 +++ 6 files changed, 96 insertions(+), 16 deletions(-) diff --git a/src/signals/incident-management/__tests__/saga.test.js b/src/signals/incident-management/__tests__/saga.test.js index a18d5ff0ec..e76479cbeb 100644 --- a/src/signals/incident-management/__tests__/saga.test.js +++ b/src/signals/incident-management/__tests__/saga.test.js @@ -128,7 +128,7 @@ describe('signals/incident-management/saga', () => { const filter = { name: 'filter', refresh: false } const page = 2 const ordering = '-created_at' - const incidents = [{}, {}] + const incidents = { 0: {}, 1: {}, orderedAs: '-created_at' } const params = { test: 'test' } const filterParams = { page, @@ -182,7 +182,7 @@ describe('signals/incident-management/saga', () => { [select(makeSelectActiveFilter), {}], [matchers.call.fn(authCall), []], ]) - .put(requestIncidentsSuccess([])) + .put(requestIncidentsSuccess({ orderedAs: '' })) .dispatch({ type: CLEAR_FILTERS }) .silentRun()) @@ -192,7 +192,7 @@ describe('signals/incident-management/saga', () => { [select(makeSelectActiveFilter), {}], [matchers.call.fn(authCall), []], ]) - .put(requestIncidentsSuccess([])) + .put(requestIncidentsSuccess({ orderedAs: '' })) .dispatch({ type: PAGE_CHANGED, payload: 4 }) .silentRun()) @@ -202,7 +202,7 @@ describe('signals/incident-management/saga', () => { [select(makeSelectActiveFilter), {}], [matchers.call.fn(authCall), []], ]) - .put(requestIncidentsSuccess([])) + .put(requestIncidentsSuccess({ orderedAs: '' })) .dispatch({ type: ORDERING_CHANGED, payload: 'incident-id-in-asc-order', @@ -225,7 +225,7 @@ describe('signals/incident-management/saga', () => { .select(makeSelectFilterParams) .call.like(authCall, CONFIGURATION.SEARCH_ENDPOINT, { q }) .not.put(push('/manage/incidents')) - .put(searchIncidentsSuccess(incidentsJSON)) + .put(searchIncidentsSuccess({ ...incidentsJSON, orderedAs: '' })) .run() }) diff --git a/src/signals/incident-management/containers/IncidentOverviewPage/index.js b/src/signals/incident-management/containers/IncidentOverviewPage/index.js index 2b576ff4d0..cf4eefee4e 100644 --- a/src/signals/incident-management/containers/IncidentOverviewPage/index.js +++ b/src/signals/incident-management/containers/IncidentOverviewPage/index.js @@ -14,7 +14,6 @@ import LoadingIndicator from 'components/LoadingIndicator' import Modal from 'components/Modal' import OverviewMap from 'components/OverviewMap' import { showGlobalNotification } from 'containers/App/actions' -import { VARIANT_NOTICE, TYPE_GLOBAL } from 'containers/App/constants' import { makeSelectSearchQuery } from 'containers/App/selectors' import MapContext from 'containers/MapContext' import useEventEmitter from 'hooks/useEventEmitter' @@ -32,6 +31,7 @@ import MyFilters from 'signals/incident-management/containers/MyFilters' import dataLists from 'signals/incident-management/definitions' import { makeSelectActiveFilter, + makeSelectErrorMessage, makeSelectFiltersOnOverview, makeSelectIncidents, makeSelectOrdering, @@ -53,6 +53,12 @@ import { StyledButton, StyledPagination, } from './styled' +import { + TYPE_GLOBAL, + TYPE_LOCAL, + VARIANT_ERROR, + VARIANT_NOTICE, +} from '../../../../containers/Notification/constants' import { MAP_URL } from '../../routes' import FilterTagList from '../FilterTagList/FilterTagList' @@ -68,6 +74,7 @@ export const IncidentOverviewPageContainerComponent = ({ orderingChangedAction, page, pageChangedAction, + errorMessage, }) => { const location = useLocation() const dispatch = useDispatch() @@ -98,6 +105,19 @@ export const IncidentOverviewPageContainerComponent = ({ const disableFilters = hasActiveOrdering || searchQueryIncidents const disableSorting = hasActiveFilters || searchQueryIncidents + useEffect(() => { + if (errorMessage) { + dispatch( + showGlobalNotification({ + title: 'Let op, het sorteren is niet gelukt', + message: errorMessage, + variant: VARIANT_ERROR, + type: TYPE_LOCAL, + }) + ) + } + }, [errorMessage, dispatch]) + const showNotification = useCallback(() => { dispatch( showGlobalNotification({ @@ -278,7 +298,7 @@ export const IncidentOverviewPageContainerComponent = ({ {canRenderList && ( diff --git a/src/signals/incident-management/containers/IncidentOverviewPage/index.test.js b/src/signals/incident-management/containers/IncidentOverviewPage/index.test.js index 6a6b08e0b5..ae1f48b72e 100644 --- a/src/signals/incident-management/containers/IncidentOverviewPage/index.test.js +++ b/src/signals/incident-management/containers/IncidentOverviewPage/index.test.js @@ -4,6 +4,7 @@ import { act, fireEvent, render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import Adapter from '@wojtekmaj/enzyme-adapter-react-17' import Enzyme, { mount } from 'enzyme' +import fetchMock from 'jest-fetch-mock' import { mocked } from 'jest-mock' import * as reactRedux from 'react-redux' import { disablePageScroll, enablePageScroll } from 'scroll-lock' @@ -17,12 +18,6 @@ import IncidentOverviewPage, { IncidentOverviewPageContainerComponent } from '.' Enzyme.configure({ adapter: new Adapter() }) -const mockedGlobalNotification = mocked( - actions.showGlobalNotification, - true -).mockReturnValue({ - type: 'sia/App/SHOW_GLOBAL_NOTIFICATION', -}) jest.mock('containers/App/actions') jest.mock('scroll-lock') @@ -87,6 +82,10 @@ describe('signals/incident-management/containers/IncidentOverviewPage', () => { clearFiltersAction: jest.fn(), } }) + afterEach(() => { + jest.resetAllMocks() + fetchMock.resetMocks() + }) it('should render modal buttons', () => { render( @@ -219,6 +218,39 @@ describe('signals/incident-management/containers/IncidentOverviewPage', () => { expect(screen.getByText('Geen meldingen')).toBeInTheDocument() }) + it('should show notification when sorting is not working', async () => { + const mockedGlobalNotification = mocked( + actions.showGlobalNotification, + true + ).mockReturnValue({ + type: 'sia/App/SHOW_GLOBAL_NOTIFICATION', + }) + + const incidents = generateIncidents() + + render( + withAppContext( + + ) + ) + await waitFor(() => { + expect(mockedGlobalNotification).toHaveBeenCalledWith( + expect.objectContaining({ + title: 'Let op, het sorteren is niet gelukt', + message: 'testing', + }) + ) + }) + }) + it('should have props from structured selector', () => { const tree = mount(withAppContext()) @@ -440,6 +472,13 @@ describe('signals/incident-management/containers/IncidentOverviewPage', () => { }) it('should disable filter buttons when ordering is active and show notification when clicked', async () => { + const mockedGlobalNotification = mocked( + actions.showGlobalNotification, + true + ).mockReturnValue({ + type: 'sia/App/SHOW_GLOBAL_NOTIFICATION', + }) + render( withAppContext( { }) it('should disable filter buttons when search is active and show notification when clicked', async () => { + const mockedGlobalNotification = mocked( + actions.showGlobalNotification, + true + ).mockReturnValue({ + type: 'sia/App/SHOW_GLOBAL_NOTIFICATION', + }) jest.spyOn(reactRedux, 'useSelector').mockReturnValue('mock-search-query') render( diff --git a/src/signals/incident-management/reducer.js b/src/signals/incident-management/reducer.js index 7d88a02c7d..ff4116636e 100644 --- a/src/signals/incident-management/reducer.js +++ b/src/signals/incident-management/reducer.js @@ -44,12 +44,14 @@ export const initialState = fromJS({ incidents: { count: undefined, results: [], + orderedAs: '', //ordering of results }, loading: false, loadingDistricts: false, loadingFilters: false, loadingIncidents: false, - ordering: '', + ordering: '', //ordering as user wants, may have failed. Old ordering still available in incidients.orderedAs + page: 1, }) diff --git a/src/signals/incident-management/saga.js b/src/signals/incident-management/saga.js index 7e0a8967dc..657b4817aa 100644 --- a/src/signals/incident-management/saga.js +++ b/src/signals/incident-management/saga.js @@ -88,7 +88,9 @@ export function* fetchIncidents() { params ) - yield put(requestIncidentsSuccess(incidents)) + yield put( + requestIncidentsSuccess({ ...incidents, orderedAs: params.ordering }) + ) if (filter && filter.refresh) { yield put(applyFilterRefresh()) @@ -113,7 +115,7 @@ export function* searchIncidents() { ordering, }) - yield put(searchIncidentsSuccess(incidents)) + yield put(searchIncidentsSuccess({ ...incidents, orderedAs: ordering })) } catch (error) { if (error.response && error.response.status === 500) { // Getting an error response with status code 500 from the search endpoint diff --git a/src/signals/incident-management/selectors.js b/src/signals/incident-management/selectors.js index 64fb5e1eec..3ce0182c3b 100644 --- a/src/signals/incident-management/selectors.js +++ b/src/signals/incident-management/selectors.js @@ -189,6 +189,15 @@ export const makeSelectIncidents = createSelector( } ) +export const makeSelectErrorMessage = createSelector( + selectIncidentManagementDomain, + (state) => { + const obj = state.toJS() + + return obj.errorMessage + } +) + export const makeSelectIncidentsCount = createSelector( selectIncidentManagementDomain, (state) => {