diff --git a/src/components/FunctionalArea/MapNavigation/MapNavigation.component.test.tsx b/src/components/FunctionalArea/MapNavigation/MapNavigation.component.test.tsx index 2af7f3fb90190b2ffa0dc1f822138ceab27c2396..35c89a0c07e0950eda63195256750d1e49ecc129 100644 --- a/src/components/FunctionalArea/MapNavigation/MapNavigation.component.test.tsx +++ b/src/components/FunctionalArea/MapNavigation/MapNavigation.component.test.tsx @@ -10,6 +10,7 @@ import { } from '@/utils/testing/getReduxWrapperWithStore'; import { act, render, screen, within } from '@testing-library/react'; import { HISTAMINE_MAP_ID, MAIN_MAP_ID } from '@/constants/mocks'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { MapNavigation } from './MapNavigation.component'; const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => { @@ -35,6 +36,7 @@ describe('MapNavigation - component', () => { loading: 'succeeded', error: { message: '', name: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }); @@ -55,6 +57,7 @@ describe('MapNavigation - component', () => { loading: 'succeeded', error: { message: '', name: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }); @@ -81,6 +84,7 @@ describe('MapNavigation - component', () => { loading: 'succeeded', error: { message: '', name: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }); @@ -111,6 +115,7 @@ describe('MapNavigation - component', () => { modelId: HISTAMINE_MAP_ID, }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, loading: 'succeeded', error: { message: '', name: '' }, }, @@ -157,6 +162,7 @@ describe('MapNavigation - component', () => { modelId: HISTAMINE_MAP_ID, }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, loading: 'succeeded', error: { message: '', name: '' }, }, @@ -183,6 +189,7 @@ describe('MapNavigation - component', () => { modelId: HISTAMINE_MAP_ID, }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, loading: 'succeeded', error: { message: '', name: '' }, }, @@ -207,6 +214,7 @@ describe('MapNavigation - component', () => { modelId: HISTAMINE_MAP_ID, }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, loading: 'succeeded', error: { message: '', name: '' }, }, diff --git a/src/components/FunctionalArea/Modal/OverviewImagesModal/utils/useOverviewImageLinkActions.test.ts b/src/components/FunctionalArea/Modal/OverviewImagesModal/utils/useOverviewImageLinkActions.test.ts index afcf6cb18d7c9f9e4a657c13f07737988490ed97..2df46d7cbecdabf42c61a89f1ad3763da2e2b7ee 100644 --- a/src/components/FunctionalArea/Modal/OverviewImagesModal/utils/useOverviewImageLinkActions.test.ts +++ b/src/components/FunctionalArea/Modal/OverviewImagesModal/utils/useOverviewImageLinkActions.test.ts @@ -17,6 +17,7 @@ import { OverviewImageLink } from '@/types/models'; import { getReduxStoreWithActionsListener } from '@/utils/testing/getReduxStoreActionsListener'; import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore'; import { renderHook } from '@testing-library/react'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { FIRST_ARRAY_ELEMENT, NOOP, @@ -59,6 +60,7 @@ describe('useOverviewImageLinkActions - hook', () => { loading: 'succeeded', error: { name: '', message: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }); @@ -109,6 +111,7 @@ describe('useOverviewImageLinkActions - hook', () => { loading: 'succeeded', error: { name: '', message: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }); @@ -166,6 +169,7 @@ describe('useOverviewImageLinkActions - hook', () => { loading: 'succeeded', error: { name: '', message: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, models: { data: MODELS_MOCK_SHORT, @@ -244,6 +248,7 @@ describe('useOverviewImageLinkActions - hook', () => { loading: 'succeeded', error: { name: '', message: '' }, openedMaps: openedMapsInitialValueFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, models: { data: MODELS_MOCK_SHORT, @@ -348,6 +353,7 @@ describe('useOverviewImageLinkActions - hook', () => { loading: 'succeeded', error: { name: '', message: '' }, openedMaps: openedMapsInitialValueFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, models: { data: MODELS_MOCK_SHORT, @@ -405,6 +411,7 @@ describe('useOverviewImageLinkActions - hook', () => { loading: 'succeeded', error: { name: '', message: '' }, openedMaps: openedMapsInitialValueFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, models: { data: MODELS_MOCK_SHORT, @@ -466,6 +473,7 @@ describe('useOverviewImageLinkActions - hook', () => { loading: 'succeeded', error: { name: '', message: '' }, openedMaps: openedMapsInitialValueFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, models: { data: MODELS_MOCK_SHORT, @@ -529,6 +537,7 @@ describe('useOverviewImageLinkActions - hook', () => { loading: 'succeeded', error: { name: '', message: '' }, openedMaps: openedMapsInitialValueFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, models: { data: MODELS_MOCK_SHORT, diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/ElementsOnMapCell/ElementLink/ElementLink.component.test.tsx b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/ElementsOnMapCell/ElementLink/ElementLink.component.test.tsx index bb30b23fd6bff11efb14587154f2c69773143f58..9ed58cb2b84d2ad88a7fdaedc5461ee34c1eaaaf 100644 --- a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/ElementsOnMapCell/ElementLink/ElementLink.component.test.tsx +++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/ElementsOnMapCell/ElementLink/ElementLink.component.test.tsx @@ -14,6 +14,7 @@ import { import { render, screen, waitFor } from '@testing-library/react'; import { HttpStatusCode } from 'axios'; import { MockStoreEnhanced } from 'redux-mock-store'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { ElementLink } from './ElementLink.component'; const mockedAxiosNewClient = mockNetworkNewAPIResponse(); @@ -208,6 +209,7 @@ describe('ElementLink - component', () => { lastPosition: DEFAULT_POSITION, }, ], + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }, ); diff --git a/src/components/Map/Drawer/BioEntityDrawer/AssociatedSubmap/AssociatedSubmap.component.test.tsx b/src/components/Map/Drawer/BioEntityDrawer/AssociatedSubmap/AssociatedSubmap.component.test.tsx index d3880a0982b7ed610b201de0f4b8d17f853bc4b6..4cc91adc374f0eb61ccc9f266a14706386f2fe58 100644 --- a/src/components/Map/Drawer/BioEntityDrawer/AssociatedSubmap/AssociatedSubmap.component.test.tsx +++ b/src/components/Map/Drawer/BioEntityDrawer/AssociatedSubmap/AssociatedSubmap.component.test.tsx @@ -18,6 +18,7 @@ import { getReduxWrapperWithStore, } from '@/utils/testing/getReduxWrapperWithStore'; import { act, render, screen } from '@testing-library/react'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { AssociatedSubmap } from './AssociatedSubmap.component'; const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => { @@ -91,6 +92,7 @@ describe('AssociatedSubmap - component', () => { loading: 'succeeded', error: { name: '', message: '' }, openedMaps: openedMapsInitialValueFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, bioEntity: { ...BIOENTITY_INITIAL_STATE_MOCK, @@ -150,6 +152,7 @@ describe('AssociatedSubmap - component', () => { loading: 'succeeded', error: { name: '', message: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, bioEntity: { ...BIOENTITY_INITIAL_STATE_MOCK, diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesSubmapItem/BioEntitiesSubmapItem.component.test.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesSubmapItem/BioEntitiesSubmapItem.component.test.tsx index aa753d71355ef22c7b6870a35a9356b19a598453..0cc8a551c410b9c1c0fbef23d8f51e3b7387d4d7 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesSubmapItem/BioEntitiesSubmapItem.component.test.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesSubmapItem/BioEntitiesSubmapItem.component.test.tsx @@ -13,6 +13,7 @@ import { openedMapsInitialValueFixture, openedMapsThreeSubmapsFixture, } from '@/redux/map/map.fixtures'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { BioEntitiesSubmapItem } from './BioEntitiesSubmapItem.component'; const CORE_MAP_ID = 5053; @@ -100,6 +101,7 @@ describe('BioEntitiesSubmapItem - component', () => { loading: 'succeeded', error: { name: '', message: '' }, openedMaps: openedMapsInitialValueFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }); @@ -157,6 +159,7 @@ describe('BioEntitiesSubmapItem - component', () => { loading: 'succeeded', error: { name: '', message: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }); diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/PinsListItem.component.test.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/PinsListItem.component.test.tsx index b54bea432a06a379ce412848858e9715321eee26..9cfb68d823ee390908778fe25bb3c5a69fefcf72 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/PinsListItem.component.test.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/PinsListItem.component.test.tsx @@ -11,6 +11,7 @@ import { initialMapDataFixture, openedMapsThreeSubmapsFixture } from '@/redux/ma import { MODELS_DATA_MOCK_WITH_MAIN_MAP } from '@/redux/models/models.mock'; import { getReduxStoreWithActionsListener } from '@/utils/testing/getReduxStoreActionsListener'; import { MockStoreEnhanced } from 'redux-mock-store'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { PinTypeWithNone } from '../PinsList.types'; import { PinsListItem } from './PinsListItem.component'; @@ -37,6 +38,7 @@ const INITIAL_STORE_STATE: InitialStoreState = { loading: 'succeeded', error: { message: '', name: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }; @@ -169,6 +171,7 @@ describe('PinsListItem - component ', () => { loading: 'succeeded', error: { message: '', name: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }, ); @@ -202,6 +205,7 @@ describe('PinsListItem - component ', () => { loading: 'succeeded', error: { message: '', name: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }, ); diff --git a/src/components/Map/Drawer/SubmapsDrawer/SubmapsDrawer.test.tsx b/src/components/Map/Drawer/SubmapsDrawer/SubmapsDrawer.test.tsx index 42754f4eb9456f4a661c9879116dacbe75b1e705..825bccfd7441dd09caaa53cd50835a3434179759 100644 --- a/src/components/Map/Drawer/SubmapsDrawer/SubmapsDrawer.test.tsx +++ b/src/components/Map/Drawer/SubmapsDrawer/SubmapsDrawer.test.tsx @@ -11,6 +11,7 @@ import { openedMapsInitialValueFixture, openedMapsThreeSubmapsFixture, } from '@/redux/map/map.fixtures'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { SubmapsDrawer } from './SubmapsDrawer'; const MAIN_MAP_ID = 5053; @@ -64,6 +65,7 @@ describe('SubmapsDrawer - component', () => { loading: 'succeeded', error: { name: '', message: '' }, openedMaps: openedMapsInitialValueFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }); @@ -109,6 +111,7 @@ describe('SubmapsDrawer - component', () => { openedMaps: openedMapsThreeSubmapsFixture, loading: 'succeeded', error: { name: '', message: '' }, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }); diff --git a/src/components/Map/Map.component.tsx b/src/components/Map/Map.component.tsx index 67b7187c64b3d5a96784e8e713ed4ba6747d02c0..d0927b3ae54f61399bddcae328192bb7eeea5686 100644 --- a/src/components/Map/Map.component.tsx +++ b/src/components/Map/Map.component.tsx @@ -3,18 +3,23 @@ import { Drawer } from '@/components/Map/Drawer'; import { Legend } from '@/components/Map/Legend'; import { MapViewer } from '@/components/Map/MapViewer'; import { MapLoader } from '@/components/Map/MapLoader/MapLoader.component'; +import { MapVectorBackgroundSelector } from '@/components/Map/MapVectorBackgroundSelector/MapVectorBackgroundSelector.component'; +import { useAppSelector } from '@/redux/hooks/useAppSelector'; +import { vectorRenderingSelector } from '@/redux/models/models.selectors'; import { MapAdditionalActions } from './MapAdditionalActions'; import { MapAdditionalOptions } from './MapAdditionalOptions'; import { PluginsDrawer } from './PluginsDrawer'; export const Map = (): JSX.Element => { + const vectorRendering = useAppSelector(vectorRenderingSelector); return ( <div className="relative z-0 h-screen w-full overflow-hidden bg-black" data-testid="map-container" > <MapViewer /> - <MapAdditionalOptions /> + {!vectorRendering && <MapAdditionalOptions />} + {vectorRendering && <MapVectorBackgroundSelector />} <Drawer /> <PluginsDrawer /> <Legend /> diff --git a/src/components/Map/MapAdditionalActions/MapAdditionalActions.component.test.tsx b/src/components/Map/MapAdditionalActions/MapAdditionalActions.component.test.tsx index 06bd09feed567c3515c7d3f305bc8eecb9426e78..5fe7353f67f070fcc6c6615efc5089824b91fb54 100644 --- a/src/components/Map/MapAdditionalActions/MapAdditionalActions.component.test.tsx +++ b/src/components/Map/MapAdditionalActions/MapAdditionalActions.component.test.tsx @@ -11,6 +11,7 @@ import { import { act, render, screen } from '@testing-library/react'; import Map from 'ol/Map'; import { MockStoreEnhanced } from 'redux-mock-store'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { MapAdditionalActions } from './MapAdditionalActions.component'; import { useVisibleBioEntitiesPolygonCoordinates } from './utils/useVisibleBioEntitiesPolygonCoordinates'; @@ -145,6 +146,7 @@ describe('MapAdditionalActions - component', () => { message: '', }, openedMaps: [], + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }); diff --git a/src/components/Map/MapAdditionalActions/utils/useAdditionalActions.test.ts b/src/components/Map/MapAdditionalActions/utils/useAdditionalActions.test.ts index 898b10e0424587a0420f66981339a2940e648c13..8e9ca7df324838ca3c93f6bbc0e33b19dcf05b36 100644 --- a/src/components/Map/MapAdditionalActions/utils/useAdditionalActions.test.ts +++ b/src/components/Map/MapAdditionalActions/utils/useAdditionalActions.test.ts @@ -8,6 +8,7 @@ import { getReduxStoreWithActionsListener } from '@/utils/testing/getReduxStoreA import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore'; import { renderHook } from '@testing-library/react'; import Map from 'ol/Map'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { useAddtionalActions } from './useAdditionalActions'; import { useVisibleBioEntitiesPolygonCoordinates } from './useVisibleBioEntitiesPolygonCoordinates'; @@ -94,6 +95,7 @@ describe('useAddtionalActions - hook', () => { message: '', }, openedMaps: [], + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }, { diff --git a/src/components/Map/MapVectorBackgroundSelector/MapVectorBackgroundSelector.component.tsx b/src/components/Map/MapVectorBackgroundSelector/MapVectorBackgroundSelector.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..32af5af47762600163e1069fc76cb9b2ec892880 --- /dev/null +++ b/src/components/Map/MapVectorBackgroundSelector/MapVectorBackgroundSelector.component.tsx @@ -0,0 +1,27 @@ +import { useAppSelector } from '@/redux/hooks/useAppSelector'; +import { mapBackgroundTypeSelector } from '@/redux/map/map.selectors'; +import { twMerge } from 'tailwind-merge'; +import { MAP_BACKGROUND_TYPES } from '@/redux/map/map.constants'; +import { setMapBackgroundType } from '@/redux/map/map.slice'; +import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; +import { Select } from '@/shared/Select'; + +export const MapVectorBackgroundSelector = (): JSX.Element => { + const dispatch = useAppDispatch(); + const backgroundType = useAppSelector(mapBackgroundTypeSelector); + + const handleChange = (selectedBackgroundType: number): void => { + dispatch(setMapBackgroundType(selectedBackgroundType)); + }; + + return ( + <div className={twMerge('absolute right-6 top-[calc(64px+40px+24px)] z-10 flex')}> + <Select + options={MAP_BACKGROUND_TYPES} + selectedId={backgroundType} + onChange={handleChange} + width={100} + /> + </div> + ); +}; diff --git a/src/components/Map/MapViewer/MapViewerVector/listeners/useOlMapVectorListeners.test.ts b/src/components/Map/MapViewer/MapViewerVector/listeners/useOlMapVectorListeners.test.ts index 8b772615d3e8ecfb89d8a6b334fa7ed0e9be1470..2821fe918cb9eb8dc9bd2859dd5d4fe130517f58 100644 --- a/src/components/Map/MapViewer/MapViewerVector/listeners/useOlMapVectorListeners.test.ts +++ b/src/components/Map/MapViewer/MapViewerVector/listeners/useOlMapVectorListeners.test.ts @@ -5,6 +5,7 @@ import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithSto import { initialMapDataFixture, openedMapsThreeSubmapsFixture } from '@/redux/map/map.fixtures'; import { useOlMapVectorListeners } from '@/components/Map/MapViewer/MapViewerVector/listeners/useOlMapVectorListeners'; import { onMapLeftClick } from '@/components/Map/MapViewer/MapViewerVector/listeners/mouseClick/mouseLeftClick/onMapLeftClick'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; jest.mock('./mouseClick/mouseLeftClick/onMapLeftClick', () => ({ __esModule: true, @@ -25,6 +26,7 @@ describe('useOlMapVectorListeners - util', () => { loading: 'succeeded', error: { message: '', name: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }); diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/processModelElements.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/processModelElements.ts index 066712d01a07dfc03dc7d8ab86ebf0b9b9c3d158..357350e5e8fbff7079553c96c364515a2fd0576d 100644 --- a/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/processModelElements.ts +++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/processModelElements.ts @@ -14,6 +14,7 @@ import { BioShapesDict, LineTypeDict } from '@/redux/shapes/shapes.types'; import { OverlayOrder } from '@/redux/overlayBioEntity/overlayBioEntity.utils'; import { OverlayBioEntityRender } from '@/types/OLrendering'; import { GetOverlayBioEntityColorByAvailableProperties } from '@/components/Map/MapViewer/utils/config/overlaysLayer/useGetOverlayColor'; +import VectorSource from 'ol/source/Vector'; export default function processModelElements( modelElements: ModelElements, @@ -22,8 +23,10 @@ export default function processModelElements( groupedElementsOverlays: Record<string, Array<OverlayBioEntityRender>>, overlaysOrder: Array<OverlayOrder>, getOverlayColor: GetOverlayBioEntityColorByAvailableProperties, + vectorSource: VectorSource, mapInstance: MapInstance, pointToProjection: UsePointToProjectionResult, + mapBackgroundType: number, ): Array<MapElement | CompartmentCircle | CompartmentSquare | CompartmentPathway | Glyph> { const validElements: Array< MapElement | CompartmentCircle | CompartmentSquare | CompartmentPathway | Glyph @@ -47,6 +50,8 @@ export default function processModelElements( if (element.sboTerm === 'SBO:0000290') { const compartmentProps = { id: element.id, + complexId: element.complex, + compartmentId: element.compartment, x: element.x, y: element.y, nameX: element.nameX, @@ -68,6 +73,8 @@ export default function processModelElements( fontSize: element.fontSize, pointToProjection, mapInstance, + vectorSource, + mapBackgroundType, }; if (element.shape === 'OVAL_COMPARTMENT') { validElements.push(new CompartmentCircle(compartmentProps)); @@ -83,6 +90,8 @@ export default function processModelElements( validElements.push( new MapElement({ id: element.id, + complexId: element.complex, + compartmentId: element.compartment, sboTerm: element.sboTerm, shapes: elementShapes, x: element.x, @@ -107,12 +116,14 @@ export default function processModelElements( fontSize: element.fontSize, pointToProjection, mapInstance, + vectorSource, modifications: element.modificationResidues, lineTypes, bioShapes: shapes, overlays: groupedElementsOverlays[element.id], overlaysOrder, getOverlayColor, + mapBackgroundType, }), ); } diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts index 43fabc2100dccdca69456cecd7cf1cba462b04cb..a6c6253e03e93b6ae74672775c0bf040338474d5 100644 --- a/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts +++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts @@ -38,6 +38,7 @@ import { parseSurfaceMarkersToBioEntityRender } from '@/components/Map/MapViewer import MarkerOverlay from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/overlay/MarkerOverlay'; import processModelElements from '@/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/processModelElements'; import useDebouncedValue from '@/utils/useDebouncedValue'; +import { mapBackgroundTypeSelector } from '@/redux/map/map.selectors'; export const useOlMapReactionsLayer = ({ mapInstance, @@ -53,14 +54,18 @@ export const useOlMapReactionsLayer = ({ const lineTypes = useSelector(lineTypesSelector); const arrowTypes = useSelector(arrowTypesSelector); const overlaysOrder = useSelector(getOverlayOrderSelector); + const mapBackgroundType = useSelector(mapBackgroundTypeSelector); const currentMarkers = useAppSelector(markersSufraceOfCurrentMapDataSelector); const markersRender = parseSurfaceMarkersToBioEntityRender(currentMarkers); const bioEntities = useAppSelector(overlayBioEntitiesForCurrentModelSelector); const debouncedBioEntities = useDebouncedValue(bioEntities, 2000); + const { getOverlayBioEntityColorByAvailableProperties } = useGetOverlayColor(); const pointToProjection = usePointToProjection(); + const vectorSource = useMemo(() => new VectorSource(), []); + useEffect(() => { if (currentModelId) { dispatch(getModelElements(currentModelId)); @@ -130,11 +135,12 @@ export const useOlMapReactionsLayer = ({ arrowTypes, shapes: reactionShapes, pointToProjection, + vectorSource, mapInstance, }); return reactionObject.features; }); - }, [arrowTypes, lineTypes, mapInstance, modelReactions, pointToProjection, shapes]); + }, [arrowTypes, lineTypes, mapInstance, modelReactions, pointToProjection, shapes, vectorSource]); const elements: Array< MapElement | CompartmentCircle | CompartmentSquare | CompartmentPathway | Glyph @@ -149,18 +155,22 @@ export const useOlMapReactionsLayer = ({ groupedElementsOverlays, overlaysOrder, getOverlayBioEntityColorByAvailableProperties, + vectorSource, mapInstance, pointToProjection, + mapBackgroundType, ); }, [ modelElements, shapes, - pointToProjection, - mapInstance, lineTypes, groupedElementsOverlays, overlaysOrder, getOverlayBioEntityColorByAvailableProperties, + vectorSource, + mapInstance, + pointToProjection, + mapBackgroundType, ]); const features = useMemo(() => { @@ -174,8 +184,6 @@ export const useOlMapReactionsLayer = ({ ]; }, [elements, linesOverlaysFeatures, markerOverlaysFeatures, reactions]); - const vectorSource = useMemo(() => new VectorSource(), []); - useEffect(() => { vectorSource.clear(); vectorSource.addFeatures(features); diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers.test.ts index eee20645689fe08ed27adb4803c8e567adce9e73..fed682be2c760b0f5b505436cdae182cd2976ca4 100644 --- a/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers.test.ts +++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers.test.ts @@ -7,6 +7,7 @@ import VectorLayer from 'ol/layer/Vector'; import React from 'react'; import { useOlMap } from '@/components/Map/MapViewer/utils/useOlMap'; import { useOlMapVectorLayers } from '@/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; const useRefValue = { current: null, @@ -59,6 +60,7 @@ describe('useOlMapLayers - util', () => { message: '', }, openedMaps: OPENED_MAPS_INITIAL_STATE, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }); const dummyElement = document.createElement('div'); diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapWhiteCardLayer.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapWhiteCardLayer.test.ts index 2ee7f6996684f18cdbe388839ca6fefa91e15fac..394abc0c4a86767c4c8e2aee6d74e680fc965fae 100644 --- a/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapWhiteCardLayer.test.ts +++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapWhiteCardLayer.test.ts @@ -5,6 +5,7 @@ import { renderHook } from '@testing-library/react'; import BaseLayer from 'ol/layer/Base'; import VectorLayer from 'ol/layer/Vector'; import React from 'react'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { useOlMapWhiteCardLayer } from './useOlMapWhiteCardLayer'; const useRefValue = { @@ -58,6 +59,7 @@ describe('useOlMapWhiteCardLayer - util', () => { message: '', }, openedMaps: OPENED_MAPS_INITIAL_STATE, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }); diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon.ts index 5997a743aa0b5c5c1151d4a2cfd6dcd75d96302e..a28643fbf246c5bbd52deb2bce6dc15a825705e2 100644 --- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon.ts +++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon.ts @@ -2,7 +2,7 @@ import Polygon from 'ol/geom/Polygon'; import { Style } from 'ol/style'; import Feature, { FeatureLike } from 'ol/Feature'; -import { MultiPolygon } from 'ol/geom'; +import { Geometry, MultiPolygon } from 'ol/geom'; import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection'; import { HorizontalAlign, @@ -17,11 +17,15 @@ import { COMPLEX_SBO_TERM, MAP_ELEMENT_TYPES, } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants'; +import VectorSource from 'ol/source/Vector'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; export interface BaseMapElementProps { type: string; sboTerm?: string; id: number; + complexId?: number | null; + compartmentId: number | null; x: number; y: number; width: number; @@ -39,6 +43,8 @@ export interface BaseMapElementProps { fillColor: Color; borderColor: Color; pointToProjection: UsePointToProjectionResult; + vectorSource: VectorSource; + mapBackgroundType: number; } export default abstract class BaseMultiPolygon { @@ -48,6 +54,10 @@ export default abstract class BaseMultiPolygon { id: number; + complexId?: number | null; + + compartmentId: number | null; + x: number; y: number; @@ -88,10 +98,16 @@ export default abstract class BaseMultiPolygon { pointToProjection: UsePointToProjectionResult; + vectorSource: VectorSource; + + mapBackgroundType: number; + constructor({ type, sboTerm, id, + complexId, + compartmentId, x, y, width, @@ -109,10 +125,14 @@ export default abstract class BaseMultiPolygon { fillColor, borderColor, pointToProjection, + vectorSource, + mapBackgroundType, }: BaseMapElementProps) { this.type = type; this.sboTerm = sboTerm; this.id = id; + this.complexId = complexId; + this.compartmentId = compartmentId; this.x = x; this.y = y; this.width = width; @@ -130,6 +150,8 @@ export default abstract class BaseMultiPolygon { this.fillColor = fillColor; this.borderColor = borderColor; this.pointToProjection = pointToProjection; + this.vectorSource = vectorSource; + this.mapBackgroundType = mapBackgroundType; } protected abstract createPolygons(): void; @@ -176,25 +198,92 @@ export default abstract class BaseMultiPolygon { } return 1; }, + getMapExtent: (resolution: number): [number, number, number, number] | undefined => { + const view = mapInstance?.getView(); + const center = view?.getCenter(); + const size = mapInstance?.getSize(); + + if (!size || !center) { + return undefined; + } + const extentWidth = size[0] * resolution; + const extentHeight = size[1] * resolution; + + return [ + center[0] - extentWidth / 2, + center[1] - extentHeight / 2, + center[0] + extentWidth / 2, + center[1] + extentHeight / 2, + ]; + }, id: this.id, + complexId: this.complexId, + compartmentId: this.compartmentId, type: this.type, }); - + this.feature.setId(this.id); this.feature.setStyle(this.getStyle.bind(this)); } protected getStyle(feature: FeatureLike, resolution: number): Style | Array<Style> | void { const styles: Array<Style> = []; const getScale = feature.get('getScale'); + const getMapExtent = feature.get('getMapExtent'); let scale = 1; + let cover = false; + let coverRation: number = 1; if (getScale instanceof Function) { scale = getScale(resolution); } + let hide = false; + if (this.mapBackgroundType === MapBackgroundsEnum.SEMANTIC) { + if (getMapExtent instanceof Function && this.type === 'COMPARTMENT') { + const mapExtent = getMapExtent(resolution); + const featureExtent = feature.getGeometry()?.getExtent(); + if (featureExtent && mapExtent) { + const mapArea = + Math.abs(mapExtent[2] - mapExtent[0]) * Math.abs(mapExtent[3] - mapExtent[1]); + const compartmentArea = + Math.abs(featureExtent[2] - featureExtent[0]) * + Math.abs(featureExtent[3] - featureExtent[1]); + coverRation = compartmentArea / mapArea; + if (coverRation < 0.05 && scale < 1) { + cover = true; + } + (feature as Feature).set('filled', cover); + } + } + + let complex: Feature<Geometry> | null; + let compartment: Feature<Geometry> | null; + if (this.complexId) { + complex = this.vectorSource.getFeatureById(this.complexId); + if (complex) { + if (complex.get('hidden')) { + hide = true; + } + } + } + if (this.compartmentId) { + compartment = this.vectorSource.getFeatureById(this.compartmentId); + if (compartment) { + if (compartment.get('filled')) { + hide = true; + } + } + } + (feature as Feature).set('hidden', hide); + if (hide) { + return undefined; + } + } + let type: string; let fontSize: number; let lineWidth: number; let text: string; + let coverStyle: Style | undefined; this.styles.forEach(style => { const styleGeometry = style.getGeometry(); @@ -203,6 +292,14 @@ export default abstract class BaseMultiPolygon { text = styleGeometry.get('text'); fontSize = styleGeometry.get('fontSize'); lineWidth = styleGeometry.get('lineWidth'); + coverStyle = styleGeometry.get('coverStyle'); + } + if (cover) { + if (coverStyle) { + coverStyle.setZIndex(this.zIndex + 1000); + styles.push(coverStyle); + } + return; } if ( [MAP_ELEMENT_TYPES.MODIFICATION, MAP_ELEMENT_TYPES.TEXT].includes(type) && diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Compartment.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Compartment.ts index 9c50b1bc5f4978311c769a050a43bf2e259eeb2f..c61b16d1f3d353696a52cb3115727258280fb429 100644 --- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Compartment.ts +++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Compartment.ts @@ -14,9 +14,12 @@ import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/s import { MapInstance } from '@/types/map'; import { Color } from '@/types/models'; import { MAP_ELEMENT_TYPES } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants'; +import VectorSource from 'ol/source/Vector'; export interface CompartmentProps { id: number; + complexId?: number | null; + compartmentId: number | null; x: number; y: number; width: number; @@ -38,6 +41,8 @@ export interface CompartmentProps { borderColor: Color; pointToProjection: UsePointToProjectionResult; mapInstance: MapInstance; + vectorSource: VectorSource; + mapBackgroundType: number; } export default abstract class Compartment extends BaseMultiPolygon { @@ -53,6 +58,8 @@ export default abstract class Compartment extends BaseMultiPolygon { constructor({ id, + complexId, + compartmentId, x, y, width, @@ -74,10 +81,14 @@ export default abstract class Compartment extends BaseMultiPolygon { borderColor, pointToProjection, mapInstance, + vectorSource, + mapBackgroundType, }: CompartmentProps) { super({ type: 'COMPARTMENT', id, + complexId, + compartmentId, x, y, width, @@ -95,6 +106,8 @@ export default abstract class Compartment extends BaseMultiPolygon { fillColor, borderColor, pointToProjection, + vectorSource, + mapBackgroundType, }); this.outerWidth = outerWidth; this.innerWidth = innerWidth; @@ -108,6 +121,18 @@ export default abstract class Compartment extends BaseMultiPolygon { protected abstract getCompartmentCoords(): void; protected createPolygons(): void { + const coverPolygon = new Polygon([this.outerCoords]); + const coverStyle = new Style({ + geometry: coverPolygon, + fill: getFill({ color: rgbToHex({ ...this.fillColor, alpha: 255 }) }), + }); + coverPolygon.set('coverStyle', coverStyle); + this.styles.push( + new Style({ + geometry: coverPolygon, + }), + ); + const framePolygon = new Polygon([this.outerCoords, this.innerCoords]); framePolygon.set('type', MAP_ELEMENT_TYPES.COMPARTMENT); this.styles.push( diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.test.ts index 3a880e948c6c95a25b11bcdf1cbfc28bb0d1073f..ba61e067d0cd195436f958ac8ed6935e55d5081e 100644 --- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.test.ts +++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.test.ts @@ -17,6 +17,8 @@ import CompartmentCircle, { } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle'; import getEllipseCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords'; import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords'; +import VectorSource from 'ol/source/Vector'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; jest.mock('../text/getTextStyle'); jest.mock('../text/getTextCoords'); @@ -26,7 +28,7 @@ jest.mock('../coords/getEllipseCoords'); jest.mock('../style/getFill'); jest.mock('../style/rgbToHex'); -describe('MapElement', () => { +describe('CompartmentCircle', () => { let props: CompartmentCircleProps; beforeEach(() => { @@ -41,6 +43,8 @@ describe('MapElement', () => { }); props = { id: 1, + complexId: null, + compartmentId: null, x: 0, y: 0, width: 100, @@ -62,6 +66,8 @@ describe('MapElement', () => { nameHorizontalAlign: 'CENTER', pointToProjection: jest.fn(), mapInstance, + vectorSource: new VectorSource(), + mapBackgroundType: MapBackgroundsEnum.SEMANTIC, }; (getTextStyle as jest.Mock).mockReturnValue( diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.ts index ad8440b3acdeab2a095072364c738906807320e0..040cc8eedd4c24d911395acd306d06fcd21b2a9a 100644 --- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.ts +++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.ts @@ -14,9 +14,12 @@ import { import getEllipseCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords'; import Compartment from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Compartment'; import { Color } from '@/types/models'; +import VectorSource from 'ol/source/Vector'; export type CompartmentCircleProps = { id: number; + complexId?: number | null; + compartmentId: number | null; x: number; y: number; width: number; @@ -38,11 +41,15 @@ export type CompartmentCircleProps = { nameHorizontalAlign?: HorizontalAlign; pointToProjection: UsePointToProjectionResult; mapInstance: MapInstance; + vectorSource: VectorSource; + mapBackgroundType: number; }; export default class CompartmentCircle extends Compartment { constructor({ id, + complexId, + compartmentId, x, y, width, @@ -64,9 +71,13 @@ export default class CompartmentCircle extends Compartment { nameHorizontalAlign = 'CENTER', pointToProjection, mapInstance, + vectorSource, + mapBackgroundType, }: CompartmentCircleProps) { super({ id, + complexId, + compartmentId, x, y, width, @@ -88,6 +99,8 @@ export default class CompartmentCircle extends Compartment { borderColor, pointToProjection, mapInstance, + vectorSource, + mapBackgroundType, }); } diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.test.ts index db133e7f25649b5bf4265021a5bc500b4002abba..26bf658b0450675f590529ddca69b3bec198943b 100644 --- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.test.ts +++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.test.ts @@ -17,6 +17,8 @@ import CompartmentPathway, { } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway'; import getEllipseCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords'; import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords'; +import VectorSource from 'ol/source/Vector'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; jest.mock('../text/getTextStyle'); jest.mock('../text/getTextCoords'); @@ -26,7 +28,7 @@ jest.mock('../coords/getEllipseCoords'); jest.mock('../style/getFill'); jest.mock('../style/rgbToHex'); -describe('MapElement', () => { +describe('CompartmentPathway', () => { let props: CompartmentPathwayProps; beforeEach(() => { @@ -41,6 +43,8 @@ describe('MapElement', () => { }); props = { id: 1, + complexId: null, + compartmentId: null, x: 0, y: 0, width: 100, @@ -60,6 +64,8 @@ describe('MapElement', () => { nameHorizontalAlign: 'CENTER', pointToProjection: jest.fn(() => [10, 10]), mapInstance, + vectorSource: new VectorSource(), + mapBackgroundType: MapBackgroundsEnum.SEMANTIC, }; (getTextStyle as jest.Mock).mockReturnValue( diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.ts index bcc70aa1b1abed0e8397a6f181881c11e4d10d86..0fc1362727c426c653619aebfd6ed0a49f1335b6 100644 --- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.ts +++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.ts @@ -14,9 +14,15 @@ import Polygon from 'ol/geom/Polygon'; import BaseMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon'; import getStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStyle'; import { Color } from '@/types/models'; +import VectorSource from 'ol/source/Vector'; +import { Style } from 'ol/style'; +import getFill from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getFill'; +import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex'; export type CompartmentPathwayProps = { id: number; + complexId?: number | null; + compartmentId: number | null; x: number; y: number; width: number; @@ -36,6 +42,8 @@ export type CompartmentPathwayProps = { nameHorizontalAlign?: HorizontalAlign; pointToProjection: UsePointToProjectionResult; mapInstance: MapInstance; + vectorSource: VectorSource; + mapBackgroundType: number; }; export default class CompartmentPathway extends BaseMultiPolygon { @@ -43,6 +51,8 @@ export default class CompartmentPathway extends BaseMultiPolygon { constructor({ id, + complexId, + compartmentId, x, y, width, @@ -62,10 +72,14 @@ export default class CompartmentPathway extends BaseMultiPolygon { nameHorizontalAlign = 'CENTER', pointToProjection, mapInstance, + vectorSource, + mapBackgroundType, }: CompartmentPathwayProps) { super({ type: 'COMPARTMENT', id, + complexId, + compartmentId, x, y, width, @@ -83,6 +97,8 @@ export default class CompartmentPathway extends BaseMultiPolygon { fillColor, borderColor, pointToProjection, + vectorSource, + mapBackgroundType, }); this.outerWidth = outerWidth; this.createPolygons(); @@ -97,11 +113,16 @@ export default class CompartmentPathway extends BaseMultiPolygon { this.pointToProjection({ x: this.x + this.width, y: this.y }), this.pointToProjection({ x: this.x + this.width, y: this.y + this.height }), this.pointToProjection({ x: this.x, y: this.y + this.height }), + this.pointToProjection({ x: this.x, y: this.y }), ], ]); compartmentPolygon.set('type', MAP_ELEMENT_TYPES.COMPARTMENT); compartmentPolygon.set('lineWidth', this.outerWidth); - + const coverStyle = new Style({ + geometry: compartmentPolygon, + fill: getFill({ color: rgbToHex({ ...this.fillColor, alpha: 255 }) }), + }); + compartmentPolygon.set('coverStyle', coverStyle); this.styles.push( getStyle({ geometry: compartmentPolygon, diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.test.ts index 45c5ad453099499c2e1d45950421ebc3d4e0494b..ae4148d289a05838338d74c10dc1c1732cc03c20 100644 --- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.test.ts +++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.test.ts @@ -16,6 +16,8 @@ import CompartmentSquare, { } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare'; import getPolygonCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords'; import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords'; +import VectorSource from 'ol/source/Vector'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; jest.mock('../text/getTextStyle'); jest.mock('../text/getTextCoords'); @@ -24,7 +26,7 @@ jest.mock('../coords/getPolygonCoords'); jest.mock('../style/getFill'); jest.mock('../style/rgbToHex'); -describe('MapElement', () => { +describe('CompartmentSquare', () => { let props: CompartmentSquareProps; beforeEach(() => { @@ -39,6 +41,8 @@ describe('MapElement', () => { }); props = { id: 1, + complexId: null, + compartmentId: null, x: 0, y: 0, width: 100, @@ -60,6 +64,8 @@ describe('MapElement', () => { nameHorizontalAlign: 'CENTER', pointToProjection: jest.fn(), mapInstance, + vectorSource: new VectorSource(), + mapBackgroundType: MapBackgroundsEnum.SEMANTIC, }; (getTextStyle as jest.Mock).mockReturnValue( diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.ts index 1d71ac43429aa2ff8b7704cea75edd8ebc749522..1732c177dabca24d370522e51a3adc9c6dca5996 100644 --- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.ts +++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.ts @@ -13,9 +13,12 @@ import { import getPolygonCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords'; import Compartment from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Compartment'; import { Color } from '@/types/models'; +import VectorSource from 'ol/source/Vector'; export type CompartmentSquareProps = { id: number; + complexId?: number | null; + compartmentId: number | null; x: number; y: number; width: number; @@ -37,11 +40,15 @@ export type CompartmentSquareProps = { nameHorizontalAlign?: HorizontalAlign; pointToProjection: UsePointToProjectionResult; mapInstance: MapInstance; + vectorSource: VectorSource; + mapBackgroundType: number; }; export default class CompartmentSquare extends Compartment { constructor({ id, + complexId, + compartmentId, x, y, width, @@ -63,9 +70,13 @@ export default class CompartmentSquare extends Compartment { nameHorizontalAlign = 'CENTER', pointToProjection, mapInstance, + vectorSource, + mapBackgroundType, }: CompartmentSquareProps) { super({ id, + complexId, + compartmentId, x, y, width, @@ -87,6 +98,8 @@ export default class CompartmentSquare extends Compartment { borderColor, pointToProjection, mapInstance, + vectorSource, + mapBackgroundType, }); } diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.test.ts index 81665cc6febace22bb50a2bb07575f3a92db8ade..1611b7fe058d11ee1cacc475b0113d9667cdcdbc 100644 --- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.test.ts +++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.test.ts @@ -17,6 +17,8 @@ import { } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants'; import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords'; import { shapesFixture } from '@/models/fixtures/shapesFixture'; +import VectorSource from 'ol/source/Vector'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; jest.mock('../text/getTextStyle'); jest.mock('../text/getTextCoords'); @@ -40,6 +42,8 @@ describe('MapElement', () => { }); props = { id: 1, + complexId: null, + compartmentId: null, sboTerm: 'SBO:2313123', shapes: shapesFixture, x: 0, @@ -61,7 +65,9 @@ describe('MapElement', () => { nameHorizontalAlign: 'CENTER', pointToProjection: jest.fn(), mapInstance, + vectorSource: new VectorSource(), getOverlayColor: (): string => '#ffffff', + mapBackgroundType: MapBackgroundsEnum.SEMANTIC, }; (getTextStyle as jest.Mock).mockReturnValue( diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.ts index 575d8d77ecc585096e0bd55de662649ae0600fe2..4a0cd82c7e9ec6849d083707a0770359d8eea34e 100644 --- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.ts +++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.ts @@ -28,9 +28,12 @@ import { getPolygonLatitudeCoordinates } from '@/components/Map/MapViewer/utils/ import { ZERO } from '@/constants/common'; import { OverlayOrder } from '@/redux/overlayBioEntity/overlayBioEntity.utils'; import { GetOverlayBioEntityColorByAvailableProperties } from '@/components/Map/MapViewer/utils/config/overlaysLayer/useGetOverlayColor'; +import VectorSource from 'ol/source/Vector'; export type MapElementProps = { id: number; + complexId?: number | null; + compartmentId: number | null; sboTerm: string; shapes: Array<Shape>; x: number; @@ -55,12 +58,14 @@ export type MapElementProps = { activity?: boolean; pointToProjection: UsePointToProjectionResult; mapInstance: MapInstance; + vectorSource: VectorSource; bioShapes?: BioShapesDict; lineTypes?: LineTypeDict; modifications?: Array<Modification>; overlays?: Array<OverlayBioEntityRender>; overlaysOrder?: Array<OverlayOrder>; getOverlayColor: GetOverlayBioEntityColorByAvailableProperties; + mapBackgroundType: number; }; export default class MapElement extends BaseMultiPolygon { @@ -90,6 +95,8 @@ export default class MapElement extends BaseMultiPolygon { constructor({ id, + complexId, + compartmentId, sboTerm, shapes, x, @@ -114,17 +121,21 @@ export default class MapElement extends BaseMultiPolygon { activity, pointToProjection, mapInstance, + vectorSource, bioShapes = {}, lineTypes = {}, modifications = [], overlays = [], overlaysOrder = [], getOverlayColor, + mapBackgroundType, }: MapElementProps) { super({ type: FEATURE_TYPE.ALIAS, sboTerm, id, + complexId, + compartmentId, x, y, width, @@ -142,6 +153,8 @@ export default class MapElement extends BaseMultiPolygon { fillColor, borderColor, pointToProjection, + vectorSource, + mapBackgroundType, }); this.shapes = shapes; this.lineWidth = lineWidth; diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.test.ts index 86025da4d8f4ddcf8e7a4dd7eeafe173c60eb3a2..70c5b3e465362eb34f472320dba66678c61e4925 100644 --- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.test.ts +++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.test.ts @@ -10,6 +10,7 @@ import { shapesFixture } from '@/models/fixtures/shapesFixture'; import View from 'ol/View'; import { ArrowTypeDict, LineTypeDict } from '@/redux/shapes/shapes.types'; import { ArrowType, LineType } from '@/types/models'; +import VectorSource from 'ol/source/Vector'; describe('Layer', () => { let props: ReactionProps; @@ -42,6 +43,7 @@ describe('Layer', () => { acc[arrow.arrowType] = arrow.shapes; return acc; }, {}), + vectorSource: new VectorSource(), mapInstance, }; }); diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.ts index f81407649044d907650be43c9c1908023f2cd7da..f70035fd91486137b0c63645e976b6b9cb221392 100644 --- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.ts +++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.ts @@ -20,6 +20,7 @@ import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shape import { ArrowTypeDict, LineTypeDict } from '@/redux/shapes/shapes.types'; import { FEATURE_TYPE } from '@/constants/features'; import getScaledElementStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getScaledElementStyle'; +import VectorSource from 'ol/source/Vector'; export interface ReactionProps { id: number; @@ -33,6 +34,7 @@ export interface ReactionProps { arrowTypes: ArrowTypeDict; shapes: Array<Shape>; pointToProjection: UsePointToProjectionResult; + vectorSource: VectorSource; mapInstance: MapInstance; } @@ -59,6 +61,8 @@ export default class Reaction { pointToProjection: UsePointToProjectionResult; + vectorSource: VectorSource; + mapInstance: MapInstance; features: Array<Feature> = []; @@ -75,6 +79,7 @@ export default class Reaction { arrowTypes, shapes, pointToProjection, + vectorSource, mapInstance, }: ReactionProps) { this.id = id; @@ -88,6 +93,7 @@ export default class Reaction { this.arrowTypes = arrowTypes; this.shapes = shapes; this.pointToProjection = pointToProjection; + this.vectorSource = vectorSource; this.mapInstance = mapInstance; this.drawReaction(); @@ -323,7 +329,18 @@ export default class Reaction { return circleFeature; } + protected isAnyOfElementsHidden(): boolean { + return [...this.products, ...this.reactants, ...this.modifiers].some(reactionElement => { + const feature = this.vectorSource.getFeatureById(reactionElement.element); + return feature && feature.get('hidden'); + }); + } + protected getStyle(feature: FeatureLike, resolution: number): Style | Array<Style> | void { + if (this.isAnyOfElementsHidden()) { + return undefined; + } + const styles: Array<Style> = []; const maxZoom = this.mapInstance?.getView().get('originalMaxZoom'); const minResolution = this.mapInstance?.getView().getResolutionForZoom(maxZoom); diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle.ts index da9eed330814e1a0fb1083bf0ce3236da58d8c0b..4858fb21b6fa019b9e0f6c0a9fb77c43dd705542 100644 --- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle.ts +++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle.ts @@ -8,12 +8,14 @@ export default function getTextStyle({ color, zIndex, horizontalAlign, + overflow = true, }: { text: string; fontSize: number; color: string; zIndex: number; horizontalAlign: HorizontalAlign; + overflow?: boolean; }): Style { return new Style({ text: new Text({ @@ -25,7 +27,7 @@ export default function getTextStyle({ placement: 'point', textAlign: horizontalAlign.toLowerCase() as CanvasTextAlign, textBaseline: 'middle', - overflow: true, + overflow, }), zIndex, }); diff --git a/src/components/Map/MapViewer/utils/config/useOlMapCommonLayers.test.ts b/src/components/Map/MapViewer/utils/config/useOlMapCommonLayers.test.ts index 852f8093ee021e4863f614c8ec194cd484a7ff0e..b3117b1f2ef06d1590db2f93e3fa2537f1b1a70d 100644 --- a/src/components/Map/MapViewer/utils/config/useOlMapCommonLayers.test.ts +++ b/src/components/Map/MapViewer/utils/config/useOlMapCommonLayers.test.ts @@ -6,6 +6,7 @@ import BaseLayer from 'ol/layer/Base'; import React from 'react'; import VectorLayer from 'ol/layer/Vector'; import { useOlMapCommonLayers } from '@/components/Map/MapViewer/utils/config/useOlMapCommonLayers'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; const useRefValue = { current: null, @@ -58,6 +59,7 @@ describe('useOlMapCommonLayers - util', () => { message: '', }, openedMaps: OPENED_MAPS_INITIAL_STATE, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }); diff --git a/src/components/Map/MapViewer/utils/config/useOlMapLayers.test.ts b/src/components/Map/MapViewer/utils/config/useOlMapLayers.test.ts index ffcd6326cfbef787a7561c27b6b4a7333a731bde..b4bfec60ab66e106ed33c7a1b64c188ad33ad4de 100644 --- a/src/components/Map/MapViewer/utils/config/useOlMapLayers.test.ts +++ b/src/components/Map/MapViewer/utils/config/useOlMapLayers.test.ts @@ -6,6 +6,7 @@ import BaseLayer from 'ol/layer/Base'; import TileLayer from 'ol/layer/Tile'; import React from 'react'; import VectorLayer from 'ol/layer/Vector'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { useOlMapLayers } from './useOlMapLayers'; const useRefValue = { @@ -59,6 +60,7 @@ describe('useOlMapLayers - util', () => { message: '', }, openedMaps: OPENED_MAPS_INITIAL_STATE, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }); diff --git a/src/components/Map/MapViewer/utils/config/useOlMapTileLayer.test.ts b/src/components/Map/MapViewer/utils/config/useOlMapTileLayer.test.ts index 8c5321cbbb294676336ad9404a7fb7a4251f1fd5..58e4aaa57dfe5b86e6ae60c1bbd7561cf87303e4 100644 --- a/src/components/Map/MapViewer/utils/config/useOlMapTileLayer.test.ts +++ b/src/components/Map/MapViewer/utils/config/useOlMapTileLayer.test.ts @@ -5,6 +5,7 @@ import { renderHook } from '@testing-library/react'; import BaseLayer from 'ol/layer/Base'; import TileLayer from 'ol/layer/Tile'; import React from 'react'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { useOlMapTileLayer } from './useOlMapTileLayer'; const useRefValue = { @@ -58,6 +59,7 @@ describe('useOlMapTileLayer - util', () => { message: '', }, openedMaps: OPENED_MAPS_INITIAL_STATE, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }); diff --git a/src/components/Map/MapViewer/utils/config/useOlMapView.test.ts b/src/components/Map/MapViewer/utils/config/useOlMapView.test.ts index 1b52b84c6ef55181b3415866093efc806539e979..d630b12bc4056814dbcf76c16ada0d4e699aa864 100644 --- a/src/components/Map/MapViewer/utils/config/useOlMapView.test.ts +++ b/src/components/Map/MapViewer/utils/config/useOlMapView.test.ts @@ -11,6 +11,7 @@ import { act, renderHook, waitFor } from '@testing-library/react'; import { View } from 'ol'; import Map from 'ol/Map'; import React from 'react'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { useOlMap } from '../useOlMap'; import { useOlMapView } from './useOlMapView'; @@ -95,6 +96,7 @@ describe('useOlMapView - util', () => { message: '', }, openedMaps: OPENED_MAPS_INITIAL_STATE, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, backgrounds: BACKGROUND_INITIAL_STATE_MOCK, }); diff --git a/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.test.ts b/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.test.ts index 340b61de1553e9f6523c5b71c744e0de8838e2eb..94a53559e255fb33787f6512b4c4605ddcf69c62 100644 --- a/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.test.ts +++ b/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.test.ts @@ -3,6 +3,7 @@ import { renderHook } from '@testing-library/react'; import { View } from 'ol'; import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore'; import { initialMapDataFixture, openedMapsThreeSubmapsFixture } from '@/redux/map/map.fixtures'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import * as singleClickListener from './mapSingleClick/onMapSingleClick'; import * as positionListener from './onMapPositionChange'; import { useOlMapListeners } from './useOlMapListeners'; @@ -31,6 +32,7 @@ describe('useOlMapListeners - util', () => { loading: 'succeeded', error: { message: '', name: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }); diff --git a/src/redux/map/map.constants.ts b/src/redux/map/map.constants.ts index 3b9e9e530e5ee7b93e2c5e7141b7d79b74ab72fc..90648de6c76b7eab6ed8e70fab2188f9cc15e28f 100644 --- a/src/redux/map/map.constants.ts +++ b/src/redux/map/map.constants.ts @@ -6,10 +6,16 @@ import { DEFAULT_TILE_SIZE, } from '@/constants/map'; import { Point } from '@/types/map'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { MapData, MapState, OppenedMap } from './map.types'; export const MAIN_MAP = 'Main map'; +export const MAP_BACKGROUND_TYPES = [ + { id: MapBackgroundsEnum.NETWORK, name: 'Network' }, + { id: MapBackgroundsEnum.SEMANTIC, name: 'Semantic' }, +]; + export const MODEL_ID_DEFAULT: number = 0; export const BACKGROUND_ID_DEFAULT: number = 0; @@ -67,6 +73,7 @@ export const MAP_INITIAL_STATE: MapState = { loading: 'idle', error: { name: '', message: '' }, openedMaps: OPENED_MAPS_INITIAL_STATE, + backgroundType: MapBackgroundsEnum.NETWORK, }; export const INIT_MAP_SIZE_MODEL_ID_ERROR_PREFIX = 'Failed to initialize map size and model ID'; diff --git a/src/redux/map/map.enums.ts b/src/redux/map/map.enums.ts new file mode 100644 index 0000000000000000000000000000000000000000..8483ca335ea708e702baaa16b52a19876bff478b --- /dev/null +++ b/src/redux/map/map.enums.ts @@ -0,0 +1,7 @@ +/* eslint-disable no-magic-numbers */ +enum MapBackgroundsEnum { + NETWORK = 1, + SEMANTIC = 2, +} + +export default MapBackgroundsEnum; diff --git a/src/redux/map/map.fixtures.ts b/src/redux/map/map.fixtures.ts index 049bdc645285fcfcaa89883d21fe85fd86f6884e..268b7f9db2dcee081ac83c53235500c70d028863 100644 --- a/src/redux/map/map.fixtures.ts +++ b/src/redux/map/map.fixtures.ts @@ -1,5 +1,6 @@ import { DEFAULT_ERROR } from '@/constants/errors'; import { MODEL_ID_DEFAULT } from '@/redux/map/map.constants'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { MapData, MapState, OppenedMap } from './map.types'; export const openedMapsInitialValueFixture: OppenedMap[] = [ @@ -54,6 +55,7 @@ export const initialMapStateFixture: MapState = { loading: 'idle', error: DEFAULT_ERROR, openedMaps: openedMapsInitialValueFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }; export const mapStateWithCurrentlySelectedMainMapFixture: MapState = { @@ -71,4 +73,5 @@ export const mapStateWithCurrentlySelectedMainMapFixture: MapState = { loading: 'idle', error: DEFAULT_ERROR, openedMaps: openedMapsInitialValueFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }; diff --git a/src/redux/map/map.reducers.ts b/src/redux/map/map.reducers.ts index f193e7536e94936d2662d7bb69f102f2149f569c..5ae6707d283f8a037a537100d8f99d6dd1e23d62 100644 --- a/src/redux/map/map.reducers.ts +++ b/src/redux/map/map.reducers.ts @@ -1,7 +1,7 @@ import { DEFAULT_ZOOM } from '@/constants/map'; import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus'; -import { ActionReducerMapBuilder } from '@reduxjs/toolkit'; -import { getPointMerged } from '../../utils/object/getPointMerged'; +import { ActionReducerMapBuilder, PayloadAction } from '@reduxjs/toolkit'; +import { getPointMerged } from '@/utils/object/getPointMerged'; import { initMapBackground, initMapPosition, @@ -221,3 +221,11 @@ export const initOpenedMapsReducer = (builder: ActionReducerMapBuilder<MapState> state.openedMaps = action.payload; }); }; + +export const setMapBackgroundTypeReducer = ( + state: MapState, + action: PayloadAction<number>, +): void => { + const { payload } = action; + state.backgroundType = payload; +}; diff --git a/src/redux/map/map.selectors.ts b/src/redux/map/map.selectors.ts index 257f99fd81e90a0c23d606bbd5369379760a2ab6..8cb88035a07364e800f227f5361345112ab98e88 100644 --- a/src/redux/map/map.selectors.ts +++ b/src/redux/map/map.selectors.ts @@ -34,3 +34,5 @@ export const mapDataLastZoomValue = createSelector( ); export const mapDataMaxZoomValue = createSelector(mapDataSizeSelector, model => model.maxZoom); + +export const mapBackgroundTypeSelector = createSelector(mapSelector, map => map.backgroundType); diff --git a/src/redux/map/map.slice.ts b/src/redux/map/map.slice.ts index 3106a118eaa14cdcc373ea0366b74c53e121dffb..dbfd8bbe316b76183fd1f37884cf2dec618e1b2b 100644 --- a/src/redux/map/map.slice.ts +++ b/src/redux/map/map.slice.ts @@ -12,6 +12,7 @@ import { setActiveMapReducer, setLastPositionZoomReducer, setMapBackgroundReducer, + setMapBackgroundTypeReducer, setMapDataReducer, setMapPositionReducer, updateLastClickReducer, @@ -35,6 +36,7 @@ const mapSlice = createSlice({ setLastPositionZoom: setLastPositionZoomReducer, updateLastClick: updateLastClickReducer, updateLastRightClick: updateLastRightClickReducer, + setMapBackgroundType: setMapBackgroundTypeReducer, }, extraReducers: builder => { initMapPositionReducers(builder); @@ -57,6 +59,7 @@ export const { setLastPositionZoom, updateLastClick, updateLastRightClick, + setMapBackgroundType, } = mapSlice.actions; export default mapSlice.reducer; diff --git a/src/redux/map/map.types.ts b/src/redux/map/map.types.ts index 72b600dcf1a42cc08ea329d65d82144db088abaa..dc838aaa4ff842eaed325314c6149c6ab7f5836d 100644 --- a/src/redux/map/map.types.ts +++ b/src/redux/map/map.types.ts @@ -44,7 +44,9 @@ export type MapData = { }; }; -export type MapState = FetchDataState<MapData, MapData> & { openedMaps: OppenedMap[] }; +export type MapState = FetchDataState<MapData, MapData> & { openedMaps: OppenedMap[] } & { + backgroundType: number; +}; export type SetMapDataActionPayload = | (Omit<Partial<MapData>, 'position' | 'projectId'> & { diff --git a/src/redux/map/middleware/checkIfIsMapUpdateActionValid.test.ts b/src/redux/map/middleware/checkIfIsMapUpdateActionValid.test.ts index 5580c0242b1e669dddc051a2f0f181a0e0ebe8c4..1509a08944985fd50828be7932f272ba5b4864dd 100644 --- a/src/redux/map/middleware/checkIfIsMapUpdateActionValid.test.ts +++ b/src/redux/map/middleware/checkIfIsMapUpdateActionValid.test.ts @@ -1,5 +1,6 @@ import { RootState } from '@/redux/store'; import { Loading } from '@/types/loadingState'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { MAP_DATA_INITIAL_STATE, MIDDLEWARE_ALLOWED_ACTIONS, @@ -17,6 +18,7 @@ const state: Pick<RootState, 'map'> = { loading: 'idle' as Loading, error: { name: '', message: '' }, openedMaps: OPENED_MAPS_INITIAL_STATE, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }; diff --git a/src/redux/map/middleware/map.middleware.test.ts b/src/redux/map/middleware/map.middleware.test.ts index 11015648425d5752f83f22dd282a2e089a5e7871..835c5e2ddcabf9d581cbaf1760731d34fb248b7b 100644 --- a/src/redux/map/middleware/map.middleware.test.ts +++ b/src/redux/map/middleware/map.middleware.test.ts @@ -3,6 +3,7 @@ import { modelsFixture } from '@/models/fixtures/modelsFixture'; import { Loading } from '@/types/loadingState'; import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore'; import { Action } from '@reduxjs/toolkit'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { MAP_DATA_INITIAL_STATE, MIDDLEWARE_ALLOWED_ACTIONS, @@ -63,6 +64,7 @@ const { store } = getReduxWrapperWithStore({ modelId: modelsFixture[0].idObject, }, openedMaps: OPENED_MAPS_INITIAL_STATE, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, models: { ...defaultSliceState, diff --git a/src/services/pluginsManager/map/data/getBounds.test.ts b/src/services/pluginsManager/map/data/getBounds.test.ts index 99f587dab60eacdc703580964f06e5333f4f559c..b824df10325ec77f2569016ec8c813aa32af680a 100644 --- a/src/services/pluginsManager/map/data/getBounds.test.ts +++ b/src/services/pluginsManager/map/data/getBounds.test.ts @@ -2,6 +2,7 @@ import { MAP_DATA_INITIAL_STATE } from '@/redux/map/map.constants'; import { store } from '@/redux/store'; import { Map } from 'ol'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { MapManager } from '../mapManager'; import { getBounds } from './getBounds'; @@ -45,6 +46,7 @@ describe('getBounds', () => { message: '', }, openedMaps: [], + backgroundType: MapBackgroundsEnum.SEMANTIC, }, // eslint-disable-next-line @typescript-eslint/no-explicit-any }) as any, diff --git a/src/services/pluginsManager/map/fitBounds/fitBounds.test.ts b/src/services/pluginsManager/map/fitBounds/fitBounds.test.ts index 1fd5ec32d5a0c87f3327da2042ec443c7f8d7579..de852078e266a7a2342e03011f94dcd0ce6fcd69 100644 --- a/src/services/pluginsManager/map/fitBounds/fitBounds.test.ts +++ b/src/services/pluginsManager/map/fitBounds/fitBounds.test.ts @@ -2,6 +2,7 @@ import { MAP_DATA_INITIAL_STATE } from '@/redux/map/map.constants'; import { Map } from 'ol'; import { store } from '@/redux/store'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { fitBounds } from './fitBounds'; import { MapManager } from '../mapManager'; @@ -51,6 +52,7 @@ describe('fitBounds', () => { message: '', }, openedMaps: [], + backgroundType: MapBackgroundsEnum.SEMANTIC, }, // eslint-disable-next-line @typescript-eslint/no-explicit-any }) as any, @@ -101,6 +103,7 @@ describe('fitBounds', () => { message: '', }, openedMaps: [], + backgroundType: MapBackgroundsEnum.SEMANTIC, }, // eslint-disable-next-line @typescript-eslint/no-explicit-any }) as any, diff --git a/src/services/pluginsManager/map/openMap.test.ts b/src/services/pluginsManager/map/openMap.test.ts index 152f81b5f25ca97a71fde8292b315cb26f9af7ca..b3d01c8f647bbc5a8c1ef4a8b4d466d92470ee70 100644 --- a/src/services/pluginsManager/map/openMap.test.ts +++ b/src/services/pluginsManager/map/openMap.test.ts @@ -3,6 +3,7 @@ import { openMapAndSetActive, setActiveMap } from '@/redux/map/map.slice'; import { RootState, store } from '@/redux/store'; import { initialMapDataFixture, openedMapsThreeSubmapsFixture } from '@/redux/map/map.fixtures'; import { MODELS_MOCK, MODELS_MOCK_SHORT } from '@/models/mocks/modelsMock'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { PluginsEventBus } from '../pluginsEventBus'; import { openMap } from './openMap'; @@ -25,6 +26,7 @@ describe('openMap', () => { loading: 'succeeded', error: { message: '', name: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, models: { data: MODELS_MOCK_SHORT, @@ -50,6 +52,7 @@ describe('openMap', () => { loading: 'succeeded', error: { message: '', name: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, models: { data: MODELS_MOCK, @@ -77,6 +80,7 @@ describe('openMap', () => { loading: 'succeeded', error: { message: '', name: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, models: { data: MODELS_MOCK, diff --git a/src/services/pluginsManager/map/position/getCenter.test.ts b/src/services/pluginsManager/map/position/getCenter.test.ts index f9d9dd545bda215f262f039e2d9c5f2ae7349ab5..fe23d527e9cd9e816912430d615bf272c6c5333c 100644 --- a/src/services/pluginsManager/map/position/getCenter.test.ts +++ b/src/services/pluginsManager/map/position/getCenter.test.ts @@ -1,5 +1,6 @@ import { initialMapDataFixture, openedMapsThreeSubmapsFixture } from '@/redux/map/map.fixtures'; import { RootState, store } from '@/redux/store'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { getCenter } from './getCenter'; jest.mock('../../../../redux/store'); @@ -25,6 +26,7 @@ describe('getCenter - plugin method', () => { loading: 'succeeded', error: { message: '', name: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }) as RootState, ); diff --git a/src/services/pluginsManager/map/triggerSearch/triggerSearch.test.ts b/src/services/pluginsManager/map/triggerSearch/triggerSearch.test.ts index abb470aa4b1fc5e1738a4b2962b0b36b2c168431..293f9476661069b2c9edaa882a2cdb445b8e0f86 100644 --- a/src/services/pluginsManager/map/triggerSearch/triggerSearch.test.ts +++ b/src/services/pluginsManager/map/triggerSearch/triggerSearch.test.ts @@ -11,6 +11,7 @@ import { RootState, store } from '@/redux/store'; import { mockNetworkNewAPIResponse, mockNetworkResponse } from '@/utils/mockNetworkResponse'; import { waitFor } from '@testing-library/react'; import { HttpStatusCode } from 'axios'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { triggerSearch } from './triggerSearch'; import { ERROR_INVALID_MODEL_ID_TYPE } from '../../errorMessages'; @@ -29,6 +30,7 @@ const MOCK_STATE = { loading: 'succeeded', error: { message: '', name: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, configuration: CONFIGURATION_INITIAL_STORE_MOCKS, }; diff --git a/src/services/pluginsManager/map/zoom/getZoom.test.ts b/src/services/pluginsManager/map/zoom/getZoom.test.ts index b7808d53163d34fa802f14c8e090119590c3c11f..669d8773b813f557294555dec1f34ea6a81c2cff 100644 --- a/src/services/pluginsManager/map/zoom/getZoom.test.ts +++ b/src/services/pluginsManager/map/zoom/getZoom.test.ts @@ -1,6 +1,7 @@ /* eslint-disable no-magic-numbers */ import { initialMapDataFixture, openedMapsThreeSubmapsFixture } from '@/redux/map/map.fixtures'; import { RootState, store } from '@/redux/store'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { getZoom } from './getZoom'; jest.mock('../../../../redux/store'); @@ -28,6 +29,7 @@ describe('getZoom - plugin method', () => { loading: 'succeeded', error: { message: '', name: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }) as RootState, ); @@ -57,6 +59,7 @@ describe('getZoom - plugin method', () => { loading: 'succeeded', error: { message: '', name: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }) as RootState, ); diff --git a/src/services/pluginsManager/map/zoom/setZoom.test.ts b/src/services/pluginsManager/map/zoom/setZoom.test.ts index 55502f302a70e74e6e7b7983e1bc12601e1071d9..2d5f8a3d2de05d808f0c92bbc033d62f25d22c32 100644 --- a/src/services/pluginsManager/map/zoom/setZoom.test.ts +++ b/src/services/pluginsManager/map/zoom/setZoom.test.ts @@ -4,6 +4,7 @@ import { initialMapDataFixture, openedMapsThreeSubmapsFixture } from '@/redux/ma import { setLastPositionZoom } from '@/redux/map/map.slice'; import { RootState, store } from '@/redux/store'; import { ZodError } from 'zod'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { setZoom } from './setZoom'; jest.mock('../../../../redux/store'); @@ -36,6 +37,7 @@ describe('setZoom - plugin method', () => { loading: 'succeeded', error: { message: '', name: '' }, openedMaps: openedMapsThreeSubmapsFixture, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }) as RootState, ); diff --git a/src/shared/Select/Select.component.test.tsx b/src/shared/Select/Select.component.test.tsx new file mode 100644 index 0000000000000000000000000000000000000000..d7db8199c6bc4f8a18fbcb273b49bd1eaa6e6f3d --- /dev/null +++ b/src/shared/Select/Select.component.test.tsx @@ -0,0 +1,41 @@ +/* eslint-disable no-magic-numbers */ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import { Select } from '.'; + +describe('Select Component', () => { + const mockOptions = [ + { id: 1, name: 'Option 1' }, + { id: 2, name: 'Option 2' }, + { id: 3, name: 'Option 3' }, + ]; + + const mockOnChange = jest.fn(); + + it('renders the Select component', () => { + render(<Select options={mockOptions} selectedId={1} onChange={mockOnChange} />); + expect(screen.getByTestId('select-component')).toBeInTheDocument(); + }); + + it('displays the selected option name', () => { + render(<Select options={mockOptions} selectedId={1} onChange={mockOnChange} />); + expect(screen.getByText('Option 1')).toBeInTheDocument(); + }); + + it('opens the dropdown when clicked', () => { + render(<Select options={mockOptions} selectedId={1} onChange={mockOnChange} />); + const toggleButton = screen.getByTestId('dropdown-button-name'); + + fireEvent.click(toggleButton); + expect(screen.getByRole('listbox')).toBeVisible(); + }); + + it('calls onChange with the correct value when an option is clicked', () => { + render(<Select options={mockOptions} selectedId={1} onChange={mockOnChange} />); + fireEvent.click(screen.getByTestId('dropdown-button-name')); + const optionToSelect = screen.getByText('Option 3'); + + fireEvent.click(optionToSelect); + expect(mockOnChange).toHaveBeenCalledWith(3); + }); +}); diff --git a/src/shared/Select/Select.component.tsx b/src/shared/Select/Select.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..aa3ab6d5fc11b0e8e0d7991a9d9e1e2eebd80f63 --- /dev/null +++ b/src/shared/Select/Select.component.tsx @@ -0,0 +1,90 @@ +import React from 'react'; +import { twMerge } from 'tailwind-merge'; +import { useSelect } from 'downshift'; +import { Icon } from '@/shared/Icon'; + +type SelectProps = { + options: Array<{ id: number; name: string }>; + selectedId: number; + onChange: (selectedId: number) => void; + width?: string | number; +}; + +export const Select = ({ + options, + selectedId, + onChange, + width = '100%', +}: SelectProps): React.JSX.Element => { + const selectedOption = options.find(option => option.id === selectedId); + + const { + isOpen, + highlightedIndex, + getToggleButtonProps, + getMenuProps, + getItemProps, + selectedItem, + } = useSelect({ + items: options, + selectedItem: selectedOption, + onSelectedItemChange: ({ selectedItem: newSelectedItem }) => { + if (newSelectedItem) { + onChange(newSelectedItem.id); + } + }, + itemToString: item => (item ? item.name : ''), + }); + + const widthStyle = typeof width === 'number' ? { width: `${width}px` } : { width }; + + return ( + <div + data-testid="select-component" + className={twMerge( + 'relative rounded-t bg-white text-xs shadow-primary', + !isOpen && 'rounded-b', + )} + style={widthStyle} + > + <div + className={twMerge( + 'flex cursor-pointer flex-row items-center justify-between rounded-t p-2', + )} + {...getToggleButtonProps()} + > + <span data-testid="dropdown-button-name" className="font-medium"> + {selectedItem ? selectedItem.name : 'Select an option'} + </span> + <Icon + name="chevron-down" + className={twMerge('arrow-button h-6 w-6 fill-primary-500', isOpen && 'rotate-180')} + /> + </div> + <ul + className={twMerge( + 'absolute z-10 overflow-auto rounded-b bg-white shadow-lg', + !isOpen && 'hidden', + )} + style={widthStyle} + {...getMenuProps()} + > + {isOpen && + options.map((item, index) => ( + <li + className={twMerge( + 'border-t', + highlightedIndex === index && 'text-primary-500', + selectedItem?.id === item.id && 'font-bold', + 'flex flex-col p-2 shadow-sm', + )} + key={item.id} + {...getItemProps({ item, index })} + > + <span>{item.name}</span> + </li> + ))} + </ul> + </div> + ); +}; diff --git a/src/shared/Select/index.tsx b/src/shared/Select/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..3be3db6ba50a75a0f4d7d331814793bdda6f8a6e --- /dev/null +++ b/src/shared/Select/index.tsx @@ -0,0 +1 @@ +export { Select } from './Select.component'; diff --git a/src/utils/map/useSetBounds.test.ts b/src/utils/map/useSetBounds.test.ts index 71ee12602b0c344b7276446cb952b1dbb1d687d9..fde34efdc5a1e3aade15b00395bc380a5a050843 100644 --- a/src/utils/map/useSetBounds.test.ts +++ b/src/utils/map/useSetBounds.test.ts @@ -4,6 +4,7 @@ import { MAP_DATA_INITIAL_STATE } from '@/redux/map/map.constants'; import { renderHook } from '@testing-library/react'; import { Map } from 'ol'; import { Coordinate } from 'ol/coordinate'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { getReduxWrapperWithStore } from '../testing/getReduxWrapperWithStore'; import { useSetBounds } from './useSetBounds'; @@ -34,6 +35,7 @@ describe('useSetBounds - hook', () => { message: '', }, openedMaps: [], + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }, { @@ -79,6 +81,7 @@ describe('useSetBounds - hook', () => { message: '', }, openedMaps: [], + backgroundType: MapBackgroundsEnum.SEMANTIC, }, }, { diff --git a/src/utils/query-manager/useReduxBusQueryManager.test.ts b/src/utils/query-manager/useReduxBusQueryManager.test.ts index adecf2597242f2a690362fdfb3db7be8b0d7c332..e71069d63bac0604541af3b4a56007ab400af912 100644 --- a/src/utils/query-manager/useReduxBusQueryManager.test.ts +++ b/src/utils/query-manager/useReduxBusQueryManager.test.ts @@ -2,6 +2,7 @@ import { MAP_DATA_INITIAL_STATE, OPENED_MAPS_INITIAL_STATE } from '@/redux/map/m import { Loading } from '@/types/loadingState'; import { renderHook, waitFor } from '@testing-library/react'; import mockRouter from 'next-router-mock'; +import MapBackgroundsEnum from '@/redux/map/map.enums'; import { getReduxWrapperWithStore } from '../testing/getReduxWrapperWithStore'; import { useReduxBusQueryManager } from './useReduxBusQueryManager'; @@ -70,6 +71,7 @@ describe('useReduxBusQueryManager - util', () => { }, }, openedMaps: OPENED_MAPS_INITIAL_STATE, + backgroundType: MapBackgroundsEnum.SEMANTIC, }, backgrounds: loadedDataMock, models: loadedDataMock,