From 12245eb5d57035123320e2462b67350ca54c0657 Mon Sep 17 00:00:00 2001 From: David Quartey <42542676+DavidQuartz@users.noreply.github.com> Date: Thu, 24 Mar 2022 10:50:50 +0000 Subject: [PATCH] Home and catalogue pages' layout breaks after multiple successive resource deletions (#888) --- .../js/components/CardGrid/CardGrid.jsx | 12 +--- .../js/components/FeaturedList/Cards.jsx | 12 +--- .../components/FeaturedList/FeaturedList.jsx | 1 + .../__tests__/ActionButtons-test.jsx | 2 +- .../js/routes/__tests__/UploadCard-test.jsx | 41 ++++++++++++++ .../client/js/routes/upload/UploadCard.jsx | 11 +++- .../js/selectors/__tests__/search-test.js | 55 +++++++++++++++++++ .../client/js/selectors/search.js | 12 ++-- .../client/js/utils/ResourceUtils.js | 7 +++ .../js/utils/__tests__/ResourceUtils-test.js | 10 +++- 10 files changed, 134 insertions(+), 29 deletions(-) create mode 100644 geonode_mapstore_client/client/js/routes/__tests__/UploadCard-test.jsx create mode 100644 geonode_mapstore_client/client/js/selectors/__tests__/search-test.js diff --git a/geonode_mapstore_client/client/js/components/CardGrid/CardGrid.jsx b/geonode_mapstore_client/client/js/components/CardGrid/CardGrid.jsx index d5f0f6ad57..f993822b06 100644 --- a/geonode_mapstore_client/client/js/components/CardGrid/CardGrid.jsx +++ b/geonode_mapstore_client/client/js/components/CardGrid/CardGrid.jsx @@ -90,25 +90,17 @@ const Cards = withResizeDetector(({ style={cardLayoutStyle === 'list' ? {} : containerStyle} > {resources.map((resource, idx) => { - const { - isProcessing, - isDeleted - } = getResourceStatuses(resource); + const { isProcessing } = getResourceStatuses(resource); // enable allowedOptions (menu cards) const allowedOptions = !isProcessing ? options .filter((opt) => hasPermissionsTo(resource?.perms, opt?.perms, 'resource')) : []; - if (isDeleted) { - return null; - } - return (
  • download.pk === resource.pk) ? true : false} /> diff --git a/geonode_mapstore_client/client/js/components/FeaturedList/Cards.jsx b/geonode_mapstore_client/client/js/components/FeaturedList/Cards.jsx index 89ee2ec044..6697d6b81e 100644 --- a/geonode_mapstore_client/client/js/components/FeaturedList/Cards.jsx +++ b/geonode_mapstore_client/client/js/components/FeaturedList/Cards.jsx @@ -68,14 +68,7 @@ const Cards = ({ style={containerStyle} > {resources.map((resource, idx) => { - const { - isProcessing, - isDeleted - } = getResourceStatuses(resource); - - if (isDeleted) { - return null; - } + const { isProcessing } = getResourceStatuses(resource); return (
  • { const [count, setCount] = useState(); + const nextIconStyles = { fontSize: '1rem', ...(!isNextPageAvailable || loading ? {color: 'grey', cursor: 'not-allowed'} : {cursor: 'pointer'}) diff --git a/geonode_mapstore_client/client/js/components/__tests__/ActionButtons-test.jsx b/geonode_mapstore_client/client/js/components/__tests__/ActionButtons-test.jsx index 8662d86a7a..36edec4d63 100644 --- a/geonode_mapstore_client/client/js/components/__tests__/ActionButtons-test.jsx +++ b/geonode_mapstore_client/client/js/components/__tests__/ActionButtons-test.jsx @@ -11,7 +11,7 @@ import ReactDOM from 'react-dom'; import expect from 'expect'; import ActionButtons from '../ActionButtons'; -describe('ALink test', () => { +describe('ActionButtons test', () => { beforeEach((done) => { document.body.innerHTML = '
    '; setTimeout(done); diff --git a/geonode_mapstore_client/client/js/routes/__tests__/UploadCard-test.jsx b/geonode_mapstore_client/client/js/routes/__tests__/UploadCard-test.jsx new file mode 100644 index 0000000000..b0b9c12a60 --- /dev/null +++ b/geonode_mapstore_client/client/js/routes/__tests__/UploadCard-test.jsx @@ -0,0 +1,41 @@ +/* + * Copyright 2022, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; +import expect from 'expect'; +import UploadCard from '../upload/UploadCard'; + +describe('upload card tests', () => { + beforeEach((done) => { + document.body.innerHTML = '
    '; + setTimeout(done); + }); + + afterEach((done) => { + ReactDOM.unmountComponentAtNode(document.getElementById('container')); + document.body.innerHTML = ''; + setTimeout(done); + }); + + it('renders upload card with defaults', () => { + ReactDOM.render(, document.getElementById('container')); + const uploadCard = document.querySelector( + '.gn-upload-card' + ); + expect(uploadCard).toExist(); + }); + + it('renders error card', () => { + ReactDOM.render(, document.getElementById('container')); + const uploadCard = document.querySelector( + '.gn-upload-card-error-message' + ); + expect(uploadCard).toExist(); + }); +}); diff --git a/geonode_mapstore_client/client/js/routes/upload/UploadCard.jsx b/geonode_mapstore_client/client/js/routes/upload/UploadCard.jsx index faf7404358..725af54627 100644 --- a/geonode_mapstore_client/client/js/routes/upload/UploadCard.jsx +++ b/geonode_mapstore_client/client/js/routes/upload/UploadCard.jsx @@ -39,7 +39,7 @@ function UploadCard({ type }) { - const { datasetMaxUploadSize, documentMaxUploadSize, maxParallelUploads } = getConfigProp('geoNodeSettings'); + const { datasetMaxUploadSize, documentMaxUploadSize, maxParallelUploads } = getConfigProp('geoNodeSettings') || {}; const maxAllowedBytes = type !== 'document' ? datasetMaxUploadSize : documentMaxUploadSize; const maxAllowedSize = Math.floor(maxAllowedBytes / (1024 * 1024)); @@ -89,7 +89,7 @@ function UploadCard({ : null} {state === 'INVALID' - ? } /> + ? } /> : null} @@ -113,4 +113,11 @@ function UploadCard({ ); } +UploadCard.defaultProps = { + name: '', + state: '', + progress: 0, + type: 'document' +}; + export default UploadCard; diff --git a/geonode_mapstore_client/client/js/selectors/__tests__/search-test.js b/geonode_mapstore_client/client/js/selectors/__tests__/search-test.js new file mode 100644 index 0000000000..aecf19ff74 --- /dev/null +++ b/geonode_mapstore_client/client/js/selectors/__tests__/search-test.js @@ -0,0 +1,55 @@ +/* + * Copyright 2022, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. +*/ + +import expect from 'expect'; +import { getSearchResults, getFeaturedResults, getTotalResources } from '../search'; + +describe('Search selectors tests', () => { + + it('should getSearchResults', () => { + const testState = { + resourceservice: { + processes: [{ resource: { pk: 1 }, processType: 'deleteResource', output: { status: 'finished' }, completed: true }] + }, + gnsearch: { + resources: [{ pk: 1, processed: true }, { pk: 2 }] + } + }; + expect(getSearchResults(testState)).toEqual([{ pk: 2 }]); + }); + + it('should getFeaturedResults', () => { + const testState = { + resourceservice: { + processes: [{ resource: { pk: 1 }, processType: 'deleteResource', output: { status: 'finished' }, completed: true }] + }, + gnsearch: { + featuredResources: { + resources: [{ pk: 1, processed: true }, { pk: 2 }] + } + } + }; + expect(getFeaturedResults(testState)).toEqual([{ pk: 2 }]); + }); + + it('should getTotalResources', () => { + const testState = { + resourceservice: { + processes: [{ resource: { pk: 1 }, processType: 'deleteResource', output: { status: 'finished' }, completed: true }] + }, + gnsearch: { + resources: [ + { pk: 1, processed: true }, { pk: 2 }, { + pk: 3, '@temporary': true + }], + total: 1 + } + }; + expect(getTotalResources(testState)).toEqual(2); + }); +}); diff --git a/geonode_mapstore_client/client/js/selectors/search.js b/geonode_mapstore_client/client/js/selectors/search.js index 05a38299f8..8aceef3c10 100644 --- a/geonode_mapstore_client/client/js/selectors/search.js +++ b/geonode_mapstore_client/client/js/selectors/search.js @@ -6,35 +6,37 @@ * LICENSE file in the root directory of this source tree. */ -import { getResourceStatuses } from '@js/utils/ResourceUtils'; +import { excludeDeletedResources } from '@js/utils/ResourceUtils'; export const getSearchResults = (state) => { const resources = state?.gnsearch?.resources || []; const processes = state?.resourceservice?.processes || []; - return resources.map((resource) => { + const searchResources = resources.map((resource) => { const resourceProcesses = processes.filter((process) => process?.resource?.pk === resource?.pk); if (resourceProcesses.length > 0) { return { ...resource, processes: resourceProcesses }; } return resource; }); + + return excludeDeletedResources(searchResources); }; export const getFeaturedResults = (state) => { const resources = state?.gnsearch?.featuredResources?.resources || []; const processes = state?.resourceservice?.processes || []; - return resources.map((resource) => { + const featuredResults = resources.map((resource) => { const resourceProcesses = processes.filter((process) => process?.resource?.pk === resource?.pk); if (resourceProcesses.length > 0) { return { ...resource, processes: resourceProcesses }; } return resource; }); + return excludeDeletedResources(featuredResults); }; export const getTotalResources = (state) => { const resources = getSearchResults(state); - const deletedResourcesCount = resources.filter(resource => getResourceStatuses(resource).isDeleted).length; const temporaryResourcesCount = resources.filter(resource => resource['@temporary']).length; - return (state?.gnsearch?.total || 0) - deletedResourcesCount + temporaryResourcesCount; + return (state?.gnsearch?.total || 0) + temporaryResourcesCount; }; diff --git a/geonode_mapstore_client/client/js/utils/ResourceUtils.js b/geonode_mapstore_client/client/js/utils/ResourceUtils.js index 9376b057eb..dd4cd5abbc 100644 --- a/geonode_mapstore_client/client/js/utils/ResourceUtils.js +++ b/geonode_mapstore_client/client/js/utils/ResourceUtils.js @@ -587,3 +587,10 @@ export const canCopyResource = (resource, user) => { const canCopy = resource?.is_copyable; return (canAdd && canCopy) ? true : false; }; + +export const excludeDeletedResources = (suppliedResources) => { + return suppliedResources.filter((resource) => { + const { isDeleted } = getResourceStatuses(resource); + return !isDeleted && resource; + }); +}; diff --git a/geonode_mapstore_client/client/js/utils/__tests__/ResourceUtils-test.js b/geonode_mapstore_client/client/js/utils/__tests__/ResourceUtils-test.js index 20c34d27ea..9930544e7e 100644 --- a/geonode_mapstore_client/client/js/utils/__tests__/ResourceUtils-test.js +++ b/geonode_mapstore_client/client/js/utils/__tests__/ResourceUtils-test.js @@ -18,7 +18,8 @@ import { compareBackgroundLayers, toMapStoreMapConfig, parseStyleName, - canCopyResource + canCopyResource, + excludeDeletedResources } from '../ResourceUtils'; describe('Test Resource Utils', () => { @@ -423,4 +424,11 @@ describe('Test Resource Utils', () => { expect(canCopyResource(resource, user)).toEqual(true); }); + + it('should test excludeDeletedResources', () => { + const resources = [{ name: 'test-1', processes: [{ processType: 'deleteResource', output: { status: 'finished' } }] }, + { name: 'test-2' }]; + + expect(excludeDeletedResources(resources)).toEqual([{ name: 'test-2' }]); + }); });