Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions geonode_mapstore_client/client/js/epics/gnresource.js
Original file line number Diff line number Diff line change
Expand Up @@ -323,9 +323,10 @@ const resourceTypes = {
newResourceObservable: (options) =>
Observable.defer(() => getNewGeoStoryConfig())
.switchMap((gnGeoStory) => {
const currentStory = options.data || {...gnGeoStory, sections: [{...gnGeoStory.sections[0], id: uuid(),
contents: [{...gnGeoStory.sections[0].contents[0], id: uuid()}]}]};
return Observable.of(
setCurrentStory(options.data || {...gnGeoStory, sections: [{...gnGeoStory.sections[0], id: uuid(),
contents: [{...gnGeoStory.sections[0].contents[0], id: uuid()}]}]}),
setCurrentStory({...currentStory, defaultGeoStoryConfig: {...currentStory}}),
setEditing(true),
setGeoStoryResource({
canEdit: true
Expand Down
276 changes: 269 additions & 7 deletions geonode_mapstore_client/client/js/selectors/__tests__/resource-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ import expect from 'expect';
import {
getViewedResourceType,
isNewResource,
isNewResourcePk,
getGeoNodeResourceDataFromGeoStory,
getGeoNodeResourceFromDashboard,
getResourceThumbnail,
updatingThumbnailResource,
isThumbnailChanged,
canEditPermissions,
canManageResourcePermissions,
isNewMapViewerResource,
isNewMapDirty,
isNewDashboardDirty,
isNewGeoStoryDirty,
defaultViewerPluginsSelector
} from '../resource';
import { ResourceTypes } from '@js/utils/ResourceUtils';
Expand Down Expand Up @@ -62,6 +65,16 @@ describe('resource selector', () => {
it('is new resource', () => {
expect(isNewResource(testState)).toBeTruthy();
});

it('is new resource by pk', () => {
let state = {...testState, gnresource: {...testState.gnresource, params: {pk: "new"}}};
expect(isNewResourcePk(state)).toBeTruthy();
state.gnresource.params.pk = '1';
expect(isNewResourcePk(state)).toBeFalsy();
state.gnresource.params = undefined;
expect(isNewResourcePk(state)).toBeFalsy();
});

it('getGeoNodeResourceDataFromGeoStory', () => {
expect(getGeoNodeResourceDataFromGeoStory(testState)).toEqual({ maps: [300], documents: [200, 100] });
});
Expand Down Expand Up @@ -92,17 +105,266 @@ describe('resource selector', () => {
expect(canManageResourcePermissions(state)).toBeFalsy();
state.gnresource.data.perms = undefined;
});
it('test isNewMapViewerResource', () => {
let state = {...testState, gnresource: {...testState.gnresource, type: ResourceTypes.VIEWER, params: {pk: "new"}}};
expect(isNewMapViewerResource(state)).toBeTruthy();
state.gnresource.params.pk = '1';
expect(isNewMapViewerResource(state)).toBeFalsy();
});
it('test defaultViewerPluginsSelector', () => {
let state = {...testState};
state.gnresource = {...state.gnresource, defaultViewerPlugins: ["TOC"]};
expect(defaultViewerPluginsSelector(state)).toEqual(["TOC"]);
state.gnresource = {...state.gnresource, defaultViewerPlugins: undefined};
expect(defaultViewerPluginsSelector(state)).toEqual([]);
});

it('test isNewMapDirty returns false when no mapConfigRawData exists', () => {
const state = {
gnresource: {
type: ResourceTypes.MAP
},
map: {
present: {
zoom: 5,
center: { x: 0, y: 0, crs: 'EPSG:4326' }
}
}
// No mapConfigRawData
};
expect(isNewMapDirty(state)).toBeFalsy();
});

it('test isNewMapDirty returns false when map has not changed from initial config', () => {
const initialConfig = {
version: 2,
map: {
zoom: 5,
center: { x: 0, y: 0, crs: 'EPSG:4326' },
layers: [
{ id: 'layer1', type: 'osm', group: 'background', visibility: true }
]
}
};
const state = {
gnresource: {
type: ResourceTypes.MAP
},
map: {
present: {
zoom: 5,
center: { x: 0, y: 0, crs: 'EPSG:4326' },
layers: [
{ id: 'layer1', type: 'osm', group: 'background', visibility: true }
]
}
},
layers: {
flat: [
{ id: 'layer1', type: 'osm', group: 'background', visibility: true }
]
},
mapConfigRawData: initialConfig
};
expect(isNewMapDirty(state)).toBeFalsy();
});

it('test isNewMapDirty returns true when layers are added', () => {
const initialConfig = {
version: 2,
map: {
zoom: 5,
center: { x: 0, y: 0, crs: 'EPSG:4326' },
layers: [
{ id: 'layer1', type: 'osm', group: 'background', visibility: true }
]
}
};
const state = {
gnresource: {
type: ResourceTypes.MAP
},
map: {
present: {
zoom: 5,
center: { x: 0, y: 0, crs: 'EPSG:4326' },
layers: [
{ id: 'layer1', type: 'osm', group: 'background', visibility: true },
{ id: 'layer2', type: 'wms', name: 'newLayer', visibility: true }
]
}
},
layers: {
flat: [
{ id: 'layer1', type: 'osm', group: 'background', visibility: true },
{ id: 'layer2', type: 'wms', name: 'newLayer', visibility: true }
]
},
mapConfigRawData: initialConfig
};
expect(isNewMapDirty(state)).toBeTruthy();
});

it('test isNewMapDirty ignores ellipsoid terrain layer', () => {
const initialConfig = {
version: 2,
map: {
zoom: 5,
center: { x: 0, y: 0, crs: 'EPSG:4326' },
layers: [
{ id: 'layer1', type: 'osm', group: 'background', visibility: true }
]
}
};
const state = {
gnresource: {
type: ResourceTypes.MAP
},
map: {
present: {
zoom: 5,
center: { x: 0, y: 0, crs: 'EPSG:4326' },
layers: [
{ id: 'layer1', type: 'osm', group: 'background', visibility: true },
{ id: 'ellipsoid', type: 'terrain', provider: 'ellipsoid', group: 'background' }
]
}
},
layers: {
flat: [
{ id: 'layer1', type: 'osm', group: 'background', visibility: true },
{ id: 'ellipsoid', type: 'terrain', provider: 'ellipsoid', group: 'background' }
]
},
mapConfigRawData: initialConfig
};
// Should be false because ellipsoid terrain is filtered out by compareMapChanges
expect(isNewMapDirty(state)).toBeFalsy();
});

it('test isNewDashboardDirty returns true when dashboard has widgets', () => {
const state = {
gnresource: {
type: ResourceTypes.DASHBOARD
},
widgets: {
containers: {
floating: {
widgets: [
{ id: 'widget1', widgetType: 'text' }
]
}
}
}
};
expect(isNewDashboardDirty(state)).toBeTruthy();
});

it('test isNewDashboardDirty returns false when dashboard has no widgets', () => {
const state = {
gnresource: {
type: ResourceTypes.DASHBOARD
},
widgets: {
containers: {
floating: {
widgets: []
}
}
}
};
expect(isNewDashboardDirty(state)).toBeFalsy();
});

it('test isNewGeoStoryDirty returns false for default geostory', () => {
const defaultConfig = {
sections: [{ title: 'Default Title', contents: [{ html: '' }] }],
settings: {}
};
const state = {
gnresource: {
type: ResourceTypes.GEOSTORY
},
geostory: {
currentStory: {
...defaultConfig,
defaultGeoStoryConfig: defaultConfig,
resources: []
}
}
};
expect(isNewGeoStoryDirty(state)).toBeFalsy();
});

it('test isNewGeoStoryDirty returns true when geostory has multiple sections', () => {
const defaultConfig = {
sections: [{ title: 'Default Title', contents: [{ html: '' }] }],
settings: {}
};
const state = {
gnresource: {
type: ResourceTypes.GEOSTORY
},
geostory: {
currentStory: {
sections: [
{ title: 'Section 1', contents: [{ html: '' }] },
{ title: 'Section 2', contents: [{ html: '' }] }
],
defaultGeoStoryConfig: defaultConfig,
resources: [],
settings: {}
}
}
};
expect(isNewGeoStoryDirty(state)).toBeTruthy();
});

it('test isNewGeoStoryDirty returns true when geostory has resources', () => {
const defaultConfig = {
sections: [{ title: 'Default Title', contents: [{ html: '' }] }],
settings: {}
};
const state = {
gnresource: {
type: ResourceTypes.GEOSTORY
},
geostory: {
currentStory: {
sections: [{ title: 'Default Title', contents: [{ html: '' }] }],
defaultGeoStoryConfig: defaultConfig,
resources: [{ id: 1, type: 'map' }],
settings: {}
}
}
};
expect(isNewGeoStoryDirty(state)).toBeTruthy();
});

it('test isNewGeoStoryDirty returns true when title section has content', () => {
const defaultConfig = {
sections: [{ title: 'Default Title', contents: [{ html: '' }] }],
settings: {}
};
const state = {
gnresource: {
type: ResourceTypes.GEOSTORY
},
geostory: {
currentStory: {
sections: [{ title: 'Default Title', contents: [{ html: 'Some content here' }] }],
defaultGeoStoryConfig: defaultConfig,
resources: [],
settings: {}
}
}
};
expect(isNewGeoStoryDirty(state)).toBeTruthy();
});

it('test isNewGeoStoryDirty returns false when currentData is null', () => {
const state = {
gnresource: {
type: ResourceTypes.GEOSTORY
},
geostory: {
currentStory: null
}
};
expect(isNewGeoStoryDirty(state)).toBeFalsy();
});
});
59 changes: 53 additions & 6 deletions geonode_mapstore_client/client/js/selectors/resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -337,16 +337,63 @@ function isResourceDataEqual(state, initialData = {}, currentData = {}) {
}
}

export const isNewMapViewerResource = (state) => {
const isNew = state?.gnresource?.params?.pk === "new";
const isMapViewer = state?.gnresource?.type === ResourceTypes.VIEWER;
return isNew && isMapViewer;
export const isNewResourcePk = (state) => {
return state?.gnresource?.params?.pk === "new";
};

export const getResourceDirtyState = (state) => {
if (isNewMapViewerResource(state)) {
export const isNewMapDirty = (state) => {
const mapConfigRawData = state?.mapConfigRawData;
if (!mapConfigRawData) {
return false;
}
const currentMapData = mapSaveSelector(state);
return !compareMapChanges(mapConfigRawData, currentMapData);
};

export const isNewDashboardDirty = (state) => {
const currentData = getDataPayload(state, ResourceTypes.DASHBOARD);
return (currentData?.widgets || []).length > 0;
};

export const isNewGeoStoryDirty = (state) => {
const currentData = getDataPayload(state, ResourceTypes.GEOSTORY);
if (!currentData) return false;

const defaultConfig = currentStorySelector(state)?.defaultGeoStoryConfig ?? {};
return (
currentData.sections?.length > 1 || // More than the default title section
currentData.sections?.[0]?.contents?.[0]?.html?.trim() || // Title section has content
currentData.sections?.[0]?.title !== defaultConfig.sections?.[0]?.title || // Title changed from default
currentData.resources?.length > 0 || // Has resources
!isEqual( // Settings changed from default
omitBy(currentData.settings || {}, isNil),
omitBy(defaultConfig.settings || {}, isNil)
)
);
};
Comment on lines +363 to +373
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case shouldn't be enough to check the pendingChanges selector of GeoStory? https://github.com/geosolutions-it/MapStore2/blob/master/web/client/selectors/geostory.js#L315

If I remember correctly geostory store a flat at true as soon some modification is applied to the story

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason is that pendingChanges is updated only for existing GeoStories and is modified as a side effect of a dispatch action triggered from the GeoStory page. Since the GeoNode client uses a custom page to manage the resource, this update is never invoked.


const isNewResourceDirty = (state) => {
const resourceType = state?.gnresource?.type;

switch (resourceType) {
case ResourceTypes.MAP:
return isNewMapDirty(state);
case ResourceTypes.VIEWER:
return true;
case ResourceTypes.DASHBOARD:
return isNewDashboardDirty(state);
case ResourceTypes.GEOSTORY:
return isNewGeoStoryDirty(state);
default:
return false;
}
};

export const getResourceDirtyState = (state) => {
if (isNewResourcePk(state)) {
return isNewResourceDirty(state);
}

const canEdit = canEditPermissions(state);
const isDeleting = getCurrentResourceDeleteLoading(state);
const isCopying = getCurrentResourceCopyLoading(state);
Expand Down
Loading