diff --git a/src/components/Map/Drawer/OverlaysDrawer/GeneralOverlays/OverlayListItem/OverlayListItem.component.test.tsx b/src/components/Map/Drawer/OverlaysDrawer/GeneralOverlays/OverlayListItem/OverlayListItem.component.test.tsx index dd3cc6eaab9b49e7ce522f2b28f833d29d3f1bca..1c2e5658c78a16867bf6ec52ebbad5634fa27128 100644 --- a/src/components/Map/Drawer/OverlaysDrawer/GeneralOverlays/OverlayListItem/OverlayListItem.component.test.tsx +++ b/src/components/Map/Drawer/OverlaysDrawer/GeneralOverlays/OverlayListItem/OverlayListItem.component.test.tsx @@ -47,30 +47,56 @@ describe('OverlayListItem - component', () => { expect(screen.getByRole('button', { name: 'Download' })).toBeInTheDocument(); }); - it('should trigger view overlays on view button click and switch background to Empty if available', async () => { - const OVERLAY_ID = 21; - const { store } = renderComponent({ - map: initialMapStateFixture, - backgrounds: { ...BACKGROUND_INITIAL_STATE_MOCK, data: BACKGROUNDS_MOCK }, - overlayBioEntity: OVERLAY_BIO_ENTITY_INITIAL_STATE_MOCK, - models: { ...MODELS_INITIAL_STATE_MOCK, data: [CORE_PD_MODEL_MOCK] }, - }); - mockedAxiosNewClient - .onGet(apiPath.getOverlayBioEntity({ overlayId: OVERLAY_ID, modelId: 5053 })) - .reply(HttpStatusCode.Ok, overlayBioEntityFixture); + describe('view overlays', () => { + it('should trigger view overlays on view button click and switch background to Empty if available', async () => { + const OVERLAY_ID = 21; + const MODEL_ID = 5053; + const { store } = renderComponent({ + map: initialMapStateFixture, + backgrounds: { ...BACKGROUND_INITIAL_STATE_MOCK, data: BACKGROUNDS_MOCK }, + overlayBioEntity: OVERLAY_BIO_ENTITY_INITIAL_STATE_MOCK, + models: { ...MODELS_INITIAL_STATE_MOCK, data: [CORE_PD_MODEL_MOCK] }, + }); + mockedAxiosNewClient + .onGet(apiPath.getOverlayBioEntity({ overlayId: OVERLAY_ID, modelId: MODEL_ID })) + .reply(HttpStatusCode.Ok, overlayBioEntityFixture); + + expect(store.getState().map.data.backgroundId).toBe(DEFAULT_BACKGROUND_ID); - expect(store.getState().map.data.backgroundId).toBe(DEFAULT_BACKGROUND_ID); + const ViewButton = screen.getByRole('button', { name: 'View' }); + await act(() => { + ViewButton.click(); + }); - const ViewButton = screen.getByRole('button', { name: 'View' }); - await act(() => { - ViewButton.click(); + expect(store.getState().map.data.backgroundId).toBe(EMPTY_BACKGROUND_ID); + expect(store.getState().overlayBioEntity.data).toEqual({ + [OVERLAY_ID]: { + [MODEL_ID]: parseOverlayBioEntityToOlRenderingFormat(overlayBioEntityFixture, OVERLAY_ID), + }, + }); }); + it('should disable overlay on view button click if overlay is active', async () => { + const OVERLAY_ID = 21; + const { store } = renderComponent({ + map: { + ...initialMapStateFixture, + data: { ...initialMapStateFixture.data, backgroundId: EMPTY_BACKGROUND_ID }, + }, + backgrounds: { ...BACKGROUND_INITIAL_STATE_MOCK, data: BACKGROUNDS_MOCK }, + overlayBioEntity: { ...OVERLAY_BIO_ENTITY_INITIAL_STATE_MOCK, overlaysId: [OVERLAY_ID] }, + models: { ...MODELS_INITIAL_STATE_MOCK, data: [CORE_PD_MODEL_MOCK] }, + }); + + const ViewButton = screen.getByRole('button', { name: 'Disable' }); + await act(() => { + ViewButton.click(); + }); - expect(store.getState().map.data.backgroundId).toBe(EMPTY_BACKGROUND_ID); - expect(store.getState().overlayBioEntity.data).toEqual( - parseOverlayBioEntityToOlRenderingFormat(overlayBioEntityFixture, OVERLAY_ID), - ); + expect(store.getState().overlayBioEntity.data).toEqual([]); + expect(store.getState().overlayBioEntity.overlaysId).toEqual([]); + }); }); + // TODO implement when connecting logic to component it.skip('should trigger download overlay to PC on download button click', () => {}); }); diff --git a/src/components/Map/Drawer/OverlaysDrawer/GeneralOverlays/OverlayListItem/OverlayListItem.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/GeneralOverlays/OverlayListItem/OverlayListItem.component.tsx index 20f173fe7eb91fc7cf480e7cab43a30bc0a0187e..ab40e7cfecb3b1e240e269cb7fa79148f76fbef0 100644 --- a/src/components/Map/Drawer/OverlaysDrawer/GeneralOverlays/OverlayListItem/OverlayListItem.component.tsx +++ b/src/components/Map/Drawer/OverlaysDrawer/GeneralOverlays/OverlayListItem/OverlayListItem.component.tsx @@ -1,7 +1,5 @@ -import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; -import { getOverlayBioEntityForAllModels } from '@/redux/overlayBioEntity/overlayBioEntity.thunk'; import { Button } from '@/shared/Button'; -import { useEmptyBackground } from './hooks/useEmptyBackground'; +import { useOverlay } from './hooks/useOverlay'; interface OverlayListItemProps { name: string; @@ -10,20 +8,14 @@ interface OverlayListItemProps { export const OverlayListItem = ({ name, overlayId }: OverlayListItemProps): JSX.Element => { const onDownloadOverlay = (): void => {}; - const dispatch = useAppDispatch(); - const { setBackgroundtoEmptyIfAvailable } = useEmptyBackground(); - - const onViewOverlay = (): void => { - setBackgroundtoEmptyIfAvailable(); - dispatch(getOverlayBioEntityForAllModels({ overlayId })); - }; + const { toggleOverlay, isOverlayActive } = useOverlay(overlayId); return ( <li className="flex flex-row flex-nowrap justify-between pl-5 [&:not(:last-of-type)]:mb-4"> <span>{name}</span> <div className="flex flex-row flex-nowrap"> - <Button variantStyles="ghost" className="mr-4 max-h-8" onClick={onViewOverlay}> - View + <Button variantStyles="ghost" className="mr-4 max-h-8" onClick={toggleOverlay}> + {isOverlayActive ? 'Disable' : 'View'} </Button> <Button className="max-h-8" variantStyles="ghost" onClick={onDownloadOverlay}> Download diff --git a/src/components/Map/Drawer/OverlaysDrawer/GeneralOverlays/OverlayListItem/hooks/useOverlay.ts b/src/components/Map/Drawer/OverlaysDrawer/GeneralOverlays/OverlayListItem/hooks/useOverlay.ts new file mode 100644 index 0000000000000000000000000000000000000000..89f65ee8a2651509149b0ee34070799405e6879a --- /dev/null +++ b/src/components/Map/Drawer/OverlaysDrawer/GeneralOverlays/OverlayListItem/hooks/useOverlay.ts @@ -0,0 +1,28 @@ +import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; +import { useAppSelector } from '@/redux/hooks/useAppSelector'; +import { isOverlayActiveSelector } from '@/redux/overlayBioEntity/overlayBioEntity.selector'; +import { removeOverlayBioEntityForGivenOverlay } from '@/redux/overlayBioEntity/overlayBioEntity.slice'; +import { getOverlayBioEntityForAllModels } from '@/redux/overlayBioEntity/overlayBioEntity.thunk'; +import { useEmptyBackground } from './useEmptyBackground'; + +type UseOverlay = { + toggleOverlay: () => void; + isOverlayActive: boolean; +}; + +export const useOverlay = (overlayId: number): UseOverlay => { + const dispatch = useAppDispatch(); + const isOverlayActive = useAppSelector(state => isOverlayActiveSelector(state, overlayId)); + const { setBackgroundtoEmptyIfAvailable } = useEmptyBackground(); + + const toggleOverlay = (): void => { + if (isOverlayActive) { + dispatch(removeOverlayBioEntityForGivenOverlay({ overlayId })); + } else { + setBackgroundtoEmptyIfAvailable(); + dispatch(getOverlayBioEntityForAllModels({ overlayId })); + } + }; + + return { toggleOverlay, isOverlayActive }; +}; diff --git a/src/components/Map/Drawer/OverlaysDrawer/OverlaysDrawer.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/OverlaysDrawer.component.tsx index c64ecf7408b301eb3f5b77ed21ee963d748d5fb3..178845e85667f608f604e2a22e52930c33063679 100644 --- a/src/components/Map/Drawer/OverlaysDrawer/OverlaysDrawer.component.tsx +++ b/src/components/Map/Drawer/OverlaysDrawer/OverlaysDrawer.component.tsx @@ -1,5 +1,6 @@ import { DrawerHeading } from '@/shared/DrawerHeading'; import { GeneralOverlays } from './GeneralOverlays'; +import { OverlaysLegends } from './OverlaysLegends'; import { UserOverlays } from './UserOverlays'; export const OverlaysDrawer = (): JSX.Element => { @@ -9,6 +10,7 @@ export const OverlaysDrawer = (): JSX.Element => { <div className="h-[calc(100%-93px)] max-h-[calc(100%-93px)] overflow-y-auto"> <GeneralOverlays /> <UserOverlays /> + <OverlaysLegends /> </div> </div> ); diff --git a/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaySingleLegend/OverlaySingleLegend.component.test.tsx b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaySingleLegend/OverlaySingleLegend.component.test.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5d88155049e41751da4ead6de5ec0b85e309b5ee --- /dev/null +++ b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaySingleLegend/OverlaySingleLegend.component.test.tsx @@ -0,0 +1,34 @@ +import { BASE_API_URL, PROJECT_ID } from '@/constants'; +import { overlayFixture } from '@/models/fixtures/overlaysFixture'; +import { MapOverlay } from '@/types/models'; +import { render, screen } from '@testing-library/react'; +import { OverlaySingleLegend } from './OverlaySingleLegend.component'; + +const renderComponent = ({ overlay }: { overlay: MapOverlay }): void => { + render(<OverlaySingleLegend overlay={overlay} />); +}; + +describe('OverlaySingleLegend - component', () => { + beforeEach(() => { + renderComponent({ + overlay: { + ...overlayFixture, + name: 'overlay name', + idObject: 1234, + }, + }); + }); + + it('should render title with overlay name', () => { + expect(screen.getByText('overlay name')).toBeInTheDocument(); + }); + + it('should render image with valid src and alt', () => { + const image = screen.getByAltText('overlay name legend'); + + expect(image).toBeInTheDocument(); + expect(image.getAttribute('src')).toBe( + `${BASE_API_URL}/projects/${PROJECT_ID}/overlays/1234:downloadLegend`, + ); + }); +}); diff --git a/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaySingleLegend/OverlaySingleLegend.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaySingleLegend/OverlaySingleLegend.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..c8cce460bfea97c5a83deb45b6e9735e6ebb3b84 --- /dev/null +++ b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaySingleLegend/OverlaySingleLegend.component.tsx @@ -0,0 +1,19 @@ +/* eslint-disable @next/next/no-img-element */ +import { BASE_API_URL, PROJECT_ID } from '@/constants'; +import { MapOverlay } from '@/types/models'; + +interface Props { + overlay: MapOverlay; +} + +export const OverlaySingleLegend = ({ overlay }: Props): JSX.Element => { + const overlayName = overlay.name; + const overlayImageSrc = `${BASE_API_URL}/projects/${PROJECT_ID}/overlays/${overlay.idObject}:downloadLegend`; + + return ( + <div> + <p className="mb-5 text-sm font-semibold">{overlayName}</p> + <img src={overlayImageSrc} alt={`${overlayName} legend`} /> + </div> + ); +}; diff --git a/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaySingleLegend/index.tsx b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaySingleLegend/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..f15cfb77b0c708addbecb8dcf496545904a5d2d9 --- /dev/null +++ b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaySingleLegend/index.tsx @@ -0,0 +1 @@ +export { OverlaySingleLegend } from './OverlaySingleLegend.component'; diff --git a/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaysLegends.component.test.tsx b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaysLegends.component.test.tsx new file mode 100644 index 0000000000000000000000000000000000000000..481fe031aa5586d3b583f365cb3d6c400c0bd5be --- /dev/null +++ b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaysLegends.component.test.tsx @@ -0,0 +1,61 @@ +import { BASE_API_URL, PROJECT_ID } from '@/constants'; +import { overlaysFixture } from '@/models/fixtures/overlaysFixture'; +import { StoreType } from '@/redux/store'; +import { + InitialStoreState, + getReduxWrapperWithStore, +} from '@/utils/testing/getReduxWrapperWithStore'; +import { render, screen } from '@testing-library/react'; +import { activeOverlaysSelector } from '../../../../../redux/overlayBioEntity/overlayBioEntity.selector'; +import { OverlaysLegends } from './OverlaysLegends.component'; + +jest.mock('../../../../../redux/overlayBioEntity/overlayBioEntity.selector', () => ({ + activeOverlaysSelector: jest.fn(), +})); + +const activeOverlaysSelectorMock = activeOverlaysSelector as unknown as jest.Mock; + +const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => { + const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState); + + return ( + render( + <Wrapper> + <OverlaysLegends /> + </Wrapper>, + ), + { + store, + } + ); +}; + +describe('OverlaysLegends - component', () => { + describe('when active overlays are empty', () => { + beforeEach(() => { + activeOverlaysSelectorMock.mockImplementation(() => []); + renderComponent(); + }); + + it('should not render list of overlays legends', () => { + expect(screen.getByTestId('overlays-legends')).toBeEmptyDOMElement(); + }); + }); + + describe('when active overlays are present', () => { + beforeEach(() => { + activeOverlaysSelectorMock.mockImplementation(() => overlaysFixture); + renderComponent(); + }); + + it.each(overlaysFixture)('should render overlay legend', overlay => { + const image = screen.getByAltText(`${overlay.name} legend`); + + expect(screen.getByText(overlay.name)).toBeInTheDocument(); + expect(image).toBeInTheDocument(); + expect(image.getAttribute('src')).toBe( + `${BASE_API_URL}/projects/${PROJECT_ID}/overlays/${overlay.idObject}:downloadLegend`, + ); + }); + }); +}); diff --git a/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaysLegends.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaysLegends.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..b690ff07c72826f0286739194653d6547f7f45d2 --- /dev/null +++ b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/OverlaysLegends.component.tsx @@ -0,0 +1,15 @@ +import { activeOverlaysSelector } from '@/redux/overlayBioEntity/overlayBioEntity.selector'; +import { useSelector } from 'react-redux'; +import { OverlaySingleLegend } from './OverlaySingleLegend'; + +export const OverlaysLegends = (): JSX.Element => { + const overlays = useSelector(activeOverlaysSelector); + + return ( + <div className="p-6" data-testid="overlays-legends"> + {overlays.map(overlay => ( + <OverlaySingleLegend key={overlay.idObject} overlay={overlay} /> + ))} + </div> + ); +}; diff --git a/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/index.ts b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..75ba6111015a96203b61869be4465b6d8b5c3572 --- /dev/null +++ b/src/components/Map/Drawer/OverlaysDrawer/OverlaysLegends/index.ts @@ -0,0 +1 @@ +export { OverlaysLegends } from './OverlaysLegends.component'; diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx index d594f01698bd6bc04fc19d67b02dc196a7e22221..a0d887e4ee1bbe7d7398d3b12122fdfc18ec4d41 100644 --- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx +++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlays/UserOverlays.component.tsx @@ -14,7 +14,7 @@ export const UserOverlays = (): JSX.Element => { }; return ( - <div className="p-6"> + <div className="border-b border-b-divide p-6"> {loadingUser === 'pending' && <h1>Loading</h1>} {loadingUser !== 'pending' && !authenticatedUser && ( diff --git a/src/models/fixtures/overlaysFixture.ts b/src/models/fixtures/overlaysFixture.ts index c0a26efd4daccf2dbd062e7b37a67ef6e2d1033a..8dcdb30e2a720aa8273ce463264ffbf34c9874f1 100644 --- a/src/models/fixtures/overlaysFixture.ts +++ b/src/models/fixtures/overlaysFixture.ts @@ -8,3 +8,8 @@ export const overlaysFixture = createFixture(z.array(mapOverlay), { seed: ZOD_SEED, array: { min: 2, max: 2 }, }); + +export const overlayFixture = createFixture(mapOverlay, { + seed: ZOD_SEED, + array: { min: 1, max: 1 }, +}); diff --git a/src/redux/overlayBioEntity/overlayBioEntity.reducers.test.ts b/src/redux/overlayBioEntity/overlayBioEntity.reducers.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..ba1740c3587cdb6393760afb66a422bb9d52feab --- /dev/null +++ b/src/redux/overlayBioEntity/overlayBioEntity.reducers.test.ts @@ -0,0 +1,42 @@ +import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse'; +import { HttpStatusCode } from 'axios'; +import { + ToolkitStoreWithSingleSlice, + createStoreInstanceUsingSliceReducer, +} from '@/utils/createStoreInstanceUsingSliceReducer'; +import { overlayBioEntityFixture } from '@/models/fixtures/overlayBioEntityFixture'; +import overlayBioEntityReducer from './overlayBioEntity.slice'; +import { getOverlayBioEntity } from './overlayBioEntity.thunk'; +import { parseOverlayBioEntityToOlRenderingFormat } from './overlayBioEntity.utils'; +import { OverlaysBioEntityState } from './overlayBioEntity.types'; +import { apiPath } from '../apiPath'; + +const mockedNewAxiosClient = mockNetworkNewAPIResponse(); + +describe('Overlay Bio Entity Reducers', () => { + const OVERLAY_ID = 21; + const MODEL_ID = 27; + + let store = {} as ToolkitStoreWithSingleSlice<OverlaysBioEntityState>; + beforeEach(() => { + store = createStoreInstanceUsingSliceReducer('overlayBioEntity', overlayBioEntityReducer); + }); + + describe('getOverlayBioEntityReducer', () => { + it('should update the state correctly when getOverlayBioEntity action is dispatched', async () => { + mockedNewAxiosClient + .onGet(apiPath.getOverlayBioEntity({ overlayId: OVERLAY_ID, modelId: MODEL_ID })) + .reply(HttpStatusCode.Ok, overlayBioEntityFixture); + + const { type } = await store.dispatch( + getOverlayBioEntity({ overlayId: OVERLAY_ID, modelId: MODEL_ID }), + ); + const { data } = store.getState().overlayBioEntity; + + expect(type).toBe('overlayBioEntity/getOverlayBioEntity/fulfilled'); + expect(data[OVERLAY_ID][MODEL_ID]).toEqual( + parseOverlayBioEntityToOlRenderingFormat(overlayBioEntityFixture, OVERLAY_ID), + ); + }); + }); +}); diff --git a/src/redux/overlayBioEntity/overlayBioEntity.reducers.ts b/src/redux/overlayBioEntity/overlayBioEntity.reducers.ts index da76054be13a2b6f4de7576bdf948f59ad3c38b3..797bacd78b59026e59fa45e21d2d551bcad84569 100644 --- a/src/redux/overlayBioEntity/overlayBioEntity.reducers.ts +++ b/src/redux/overlayBioEntity/overlayBioEntity.reducers.ts @@ -1,14 +1,21 @@ import { ActionReducerMapBuilder } from '@reduxjs/toolkit'; import { getOverlayBioEntity, getOverlayBioEntityForAllModels } from './overlayBioEntity.thunk'; -import { OverlaysBioEntityState } from './overlayBioEntity.types'; +import { + OverlaysBioEntityState, + RemoveOverlayBioEntityForGivenOverlayAction, +} from './overlayBioEntity.types'; export const getOverlayBioEntityReducer = ( builder: ActionReducerMapBuilder<OverlaysBioEntityState>, ): void => { builder.addCase(getOverlayBioEntity.fulfilled, (state, action) => { if (action.payload) { - state.overlaysId = [action.meta.arg.overlayId]; - state.data.push(...action.payload); + const { overlayId, modelId } = action.meta.arg; + if (!state.data[action.meta.arg.overlayId]) { + state.data[overlayId] = {}; + } + + state.data[overlayId][modelId] = action.payload; } }); }; @@ -16,7 +23,21 @@ export const getOverlayBioEntityReducer = ( export const getOverlayBioEntityForAllModelsReducer = ( builder: ActionReducerMapBuilder<OverlaysBioEntityState>, ): void => { - builder.addCase(getOverlayBioEntityForAllModels.pending, state => { - state.data = []; + builder.addCase(getOverlayBioEntityForAllModels.pending, (state, action) => { + const { overlayId } = action.meta.arg; + state.overlaysId.push(overlayId); + state.data = { + ...state.data, // this is expection to the rule of immutability from redux-toolkit. state.data[overlayId] = {} would add null values up to overlayId value witch leads to mess in the store + [overlayId]: {}, + }; }); }; + +export const removeOverlayBioEntityForGivenOverlayReducer = ( + state: OverlaysBioEntityState, + action: RemoveOverlayBioEntityForGivenOverlayAction, +): void => { + const { overlayId } = action.payload; + state.overlaysId = state.overlaysId.filter(id => id !== overlayId); + delete state.data[overlayId]; +}; diff --git a/src/redux/overlayBioEntity/overlayBioEntity.selector.ts b/src/redux/overlayBioEntity/overlayBioEntity.selector.ts index 72c3b359fdbb43ecff2a59617090397b59e90852..bd4d219fc448586e2816ecf746b7d73d5679b184 100644 --- a/src/redux/overlayBioEntity/overlayBioEntity.selector.ts +++ b/src/redux/overlayBioEntity/overlayBioEntity.selector.ts @@ -1,6 +1,7 @@ import { createSelector } from '@reduxjs/toolkit'; -import { rootSelector } from '../root/root.selectors'; import { currentModelIdSelector } from '../models/models.selectors'; +import { overlaysDataSelector } from '../overlays/overlays.selectors'; +import { rootSelector } from '../root/root.selectors'; export const overlayBioEntitySelector = createSelector( rootSelector, @@ -12,8 +13,29 @@ export const overlayBioEntityDataSelector = createSelector( overlayBioEntity => overlayBioEntity.data, ); +export const activeOverlaysIdSelector = createSelector( + overlayBioEntitySelector, + state => state.overlaysId, +); + +const FIRST_ENTITY_INDEX = 0; +// TODO, improve selector when multioverlay algorithm comes in place export const overlayBioEntitiesForCurrentModelSelector = createSelector( overlayBioEntityDataSelector, currentModelIdSelector, - (data, currentModelId) => data.filter(entity => entity.modelId === currentModelId), + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + (data, currentModelId) => data[Object.keys(data)[FIRST_ENTITY_INDEX]]?.[currentModelId] ?? [], // temporary solution untill multioverlay algorithm comes in place +); + +export const isOverlayActiveSelector = createSelector( + [activeOverlaysIdSelector, (_, overlayId: number): number => overlayId], + (overlaysId, overlayId) => overlaysId.includes(overlayId), +); + +export const activeOverlaysSelector = createSelector( + rootSelector, + overlaysDataSelector, + (state, overlaysData) => + overlaysData.filter(overlay => isOverlayActiveSelector(state, overlay.idObject)), ); diff --git a/src/redux/overlayBioEntity/overlayBioEntity.slice.ts b/src/redux/overlayBioEntity/overlayBioEntity.slice.ts index f25d3ed6e81ac34f12cad62d428f694865ff5e46..ae21322ce6147194a9a4c0c7ac056fbad33a972c 100644 --- a/src/redux/overlayBioEntity/overlayBioEntity.slice.ts +++ b/src/redux/overlayBioEntity/overlayBioEntity.slice.ts @@ -2,6 +2,7 @@ import { createSlice } from '@reduxjs/toolkit'; import { getOverlayBioEntityForAllModelsReducer, getOverlayBioEntityReducer, + removeOverlayBioEntityForGivenOverlayReducer, } from './overlayBioEntity.reducers'; import { OverlaysBioEntityState } from './overlayBioEntity.types'; @@ -13,11 +14,14 @@ const initialState: OverlaysBioEntityState = { export const overlayBioEntitySlice = createSlice({ name: 'overlayBioEntity', initialState, - reducers: {}, + reducers: { + removeOverlayBioEntityForGivenOverlay: removeOverlayBioEntityForGivenOverlayReducer, + }, extraReducers: builder => { getOverlayBioEntityReducer(builder); getOverlayBioEntityForAllModelsReducer(builder); }, }); +export const { removeOverlayBioEntityForGivenOverlay } = overlayBioEntitySlice.actions; export default overlayBioEntitySlice.reducer; diff --git a/src/redux/overlayBioEntity/overlayBioEntity.types.ts b/src/redux/overlayBioEntity/overlayBioEntity.types.ts index 43eeb895696b8af7397be5013249843aa10d649a..4074058bae71559b4c40de5a3adedaa0be381049 100644 --- a/src/redux/overlayBioEntity/overlayBioEntity.types.ts +++ b/src/redux/overlayBioEntity/overlayBioEntity.types.ts @@ -1,6 +1,15 @@ import { OverlayBioEntityRender } from '@/types/OLrendering'; +import { PayloadAction } from '@reduxjs/toolkit'; export type OverlaysBioEntityState = { overlaysId: number[]; - data: OverlayBioEntityRender[]; + data: { + [overlayId: number]: { + [modelId: number]: OverlayBioEntityRender[]; + }; + }; }; + +export type RemoveOverlayBioEntityForGivenOverlayPayload = { overlayId: number }; +export type RemoveOverlayBioEntityForGivenOverlayAction = + PayloadAction<RemoveOverlayBioEntityForGivenOverlayPayload>;