Skip to content
Open
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)
)
);
};

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