diff --git a/src/components/FunctionalArea/TopBar/TopBar.component.test.tsx b/src/components/FunctionalArea/TopBar/TopBar.component.test.tsx index 87c8d158fd86b33faf52eb3705c8fc3b53c88ca8..9a5746dcae9502dbdb5840f19b5600ebcf6e0ae6 100644 --- a/src/components/FunctionalArea/TopBar/TopBar.component.test.tsx +++ b/src/components/FunctionalArea/TopBar/TopBar.component.test.tsx @@ -1,10 +1,22 @@ -import { StoreType } from '@/redux/store'; +import { AppDispatch, RootState, StoreType } from '@/redux/store'; import { InitialStoreState, getReduxWrapperWithStore, } from '@/utils/testing/getReduxWrapperWithStore'; import { render, screen } from '@testing-library/react'; import { initialStateFixture } from '@/redux/drawer/drawerFixture'; +import { MockStoreEnhanced } from 'redux-mock-store'; +import { getReduxStoreWithActionsListener } from '@/utils/testing/getReduxStoreActionsListener'; +import { PROJECT_STATE_INITIAL_MOCK } from '@/redux/project/project.mock'; +import { projectFixture } from '@/models/fixtures/projectFixture'; +import { initialMapStateFixture } from '@/redux/map/map.fixtures'; +import { + BACKGROUNDS_MOCK, + BACKGROUND_INITIAL_STATE_MOCK, +} from '@/redux/backgrounds/background.mock'; +import { FIRST_ARRAY_ELEMENT } from '@/constants/common'; +import { USER_INITIAL_STATE_MOCK } from '@/redux/user/user.mock'; +import { SEARCH_STATE_INITIAL_MOCK } from '@/redux/search/search.mock'; import { TopBar } from './TopBar.component'; const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => { @@ -22,6 +34,23 @@ const renderComponent = (initialStoreState: InitialStoreState = {}): { store: St ); }; +const renderComponentWithActionListener = ( + initialStoreState: InitialStoreState = {}, +): { store: MockStoreEnhanced<Partial<RootState>, AppDispatch> } => { + const { Wrapper, store } = getReduxStoreWithActionsListener(initialStoreState); + + return ( + render( + <Wrapper> + <TopBar /> + </Wrapper>, + ), + { + store, + } + ); +}; + describe('TopBar - component', () => { it('Should contain search bar', () => { renderComponent(); @@ -52,4 +81,54 @@ describe('TopBar - component', () => { expect(isOpen).toBe(true); expect(drawerName).toBe('overlays'); }); + it('should render browse overview images button', () => { + renderComponent(); + expect(screen.getByText('Browse overview images')).toBeInTheDocument(); + }); + + it('should open overview image modal on button click', () => { + const { store } = renderComponentWithActionListener({ + project: { + ...PROJECT_STATE_INITIAL_MOCK, + data: projectFixture, + }, + drawer: initialStateFixture, + user: USER_INITIAL_STATE_MOCK, + map: initialMapStateFixture, + search: SEARCH_STATE_INITIAL_MOCK, + backgrounds: { ...BACKGROUND_INITIAL_STATE_MOCK, data: BACKGROUNDS_MOCK }, + }); + + const overviewImageButton = screen.getByText('Browse overview images'); + overviewImageButton.click(); + + const actions = store.getActions(); + expect(actions[FIRST_ARRAY_ELEMENT]).toStrictEqual({ + payload: projectFixture.topOverviewImage?.idObject, + type: 'modal/openOverviewImagesModalById', + }); + }); + it('should disable button browse overview images if there are no overview images', () => { + const { store } = renderComponentWithActionListener({ + user: USER_INITIAL_STATE_MOCK, + search: SEARCH_STATE_INITIAL_MOCK, + drawer: initialStateFixture, + project: { + ...PROJECT_STATE_INITIAL_MOCK, + data: { + ...projectFixture, + overviewImageViews: [], + }, + }, + map: initialMapStateFixture, + backgrounds: { ...BACKGROUND_INITIAL_STATE_MOCK, data: BACKGROUNDS_MOCK }, + }); + + const overviewImageButton = screen.getByText('Browse overview images'); + expect(overviewImageButton).toBeDisabled(); + overviewImageButton.click(); + + const actions = store.getActions(); + expect(actions).toStrictEqual([]); + }); }); diff --git a/src/components/FunctionalArea/TopBar/TopBar.component.tsx b/src/components/FunctionalArea/TopBar/TopBar.component.tsx index e41b37127870f376d5ba02886b667051dea85ce2..760e1d47e4b200af8ce563d648bff2a6c6cecd75 100644 --- a/src/components/FunctionalArea/TopBar/TopBar.component.tsx +++ b/src/components/FunctionalArea/TopBar/TopBar.component.tsx @@ -3,14 +3,28 @@ import { SearchBar } from '@/components/FunctionalArea/TopBar/SearchBar'; import { openOverlaysDrawer, openSubmapsDrawer } from '@/redux/drawer/drawer.slice'; import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; import { useAppSelector } from '@/redux/hooks/useAppSelector'; -import { projectDataSelector } from '@/redux/project/project.selectors'; +import { + projectDataSelector, + projectDefaultOverviewImageIdSelector, + projectOverviewImagesSelector, +} from '@/redux/project/project.selectors'; import { Button } from '@/shared/Button'; -import { ClearAnchorsButton } from './ClearAnchorsButton'; +import { ZERO } from '@/constants/common'; +import { useSelector } from 'react-redux'; +import { openOverviewImagesModalById } from '@/redux/modal/modal.slice'; import { User } from './User'; +import { ClearAnchorsButton } from './ClearAnchorsButton'; export const TopBar = (): JSX.Element => { const dispatch = useAppDispatch(); const currentProject = useAppSelector(projectDataSelector); + const defaultOverviewImageId = useSelector(projectDefaultOverviewImageIdSelector); + const overviewImages = useSelector(projectOverviewImagesSelector); + const overviewImagesEmpty = overviewImages.length === ZERO; + + const handleBrowseOverviewImagesClick = (): void => { + dispatch(openOverviewImagesModalById(defaultOverviewImageId)); + }; const onSubmapsClick = (): void => { dispatch(openSubmapsDrawer()); @@ -21,7 +35,7 @@ export const TopBar = (): JSX.Element => { }; return ( - <div className="flex h-16 w-full flex-row items-center justify-between border-b border-font-500 border-opacity-[0.12] bg-white py-4 pr-6"> + <div className="flex h-16 w-full flex-row items-center justify-between overflow-x-auto border-b border-font-500 border-opacity-[0.12] bg-white py-4 pr-6 xl:overflow-x-visible"> <div className="flex flex-row items-center"> <div className="relative flex w-[88px] justify-center"> <User /> @@ -43,8 +57,18 @@ export const TopBar = (): JSX.Element => { Overlays </Button> </div> - <div className="bg-primary-100 px-4 py-1 text-xs leading-6 text-primary-500"> - {currentProject?.name || ''} + <div className="flex items-center"> + <Button + className="mx-4 flex-none" + onClick={handleBrowseOverviewImagesClick} + disabled={overviewImagesEmpty} + variantStyles="secondary" + > + Browse overview images + </Button> + <div className="flex-none bg-primary-100 px-4 py-1 text-xs leading-6 text-primary-500"> + {currentProject?.name || ''} + </div> </div> </div> ); diff --git a/src/components/Map/MapAdditionalOptions/BackgroundsSelector/BackgroundsSelector.component.tsx b/src/components/Map/MapAdditionalOptions/BackgroundsSelector/BackgroundsSelector.component.tsx index ca0dd73392f972a5285211dba89fb71d1a87a102..610cb3719cc4245b23c67f1f42f1ea67f4f03fd1 100644 --- a/src/components/Map/MapAdditionalOptions/BackgroundsSelector/BackgroundsSelector.component.tsx +++ b/src/components/Map/MapAdditionalOptions/BackgroundsSelector/BackgroundsSelector.component.tsx @@ -44,7 +44,7 @@ export const BackgroundSelector = (): JSX.Element => { data-testid="background-selector" className={twMerge('rounded-t bg-white text-xs shadow-primary', !isOpen && 'rounded-b')} > - <div className={twMerge('flex w-72 flex-col rounded-t py-2 pl-4 pr-3')}> + <div className={twMerge('flex w-[135px] flex-col rounded-t py-2 pl-4 pr-3')}> <div className="flex cursor-pointer flex-row items-center justify-between bg-white" {...getToggleButtonProps()} @@ -59,7 +59,7 @@ export const BackgroundSelector = (): JSX.Element => { </div> </div> <ul - className={`absolute z-10 max-h-80 w-72 overflow-scroll rounded-b bg-white p-0 ${ + className={`absolute z-10 max-h-80 w-[135px] overflow-scroll rounded-b bg-white p-0 ${ !isOpen && 'hidden' }`} {...getMenuProps()} diff --git a/src/components/Map/MapAdditionalOptions/MapAdditionalOptions.component.test.tsx b/src/components/Map/MapAdditionalOptions/MapAdditionalOptions.component.test.tsx index ff8459b49d6a8039abdb5126f4465d0874666f50..5b877e38e83b30896fe49261fa6b99c78e4d7734 100644 --- a/src/components/Map/MapAdditionalOptions/MapAdditionalOptions.component.test.tsx +++ b/src/components/Map/MapAdditionalOptions/MapAdditionalOptions.component.test.tsx @@ -1,19 +1,9 @@ -import { FIRST_ARRAY_ELEMENT } from '@/constants/common'; -import { - BACKGROUNDS_MOCK, - BACKGROUND_INITIAL_STATE_MOCK, -} from '@/redux/backgrounds/background.mock'; -import { initialMapStateFixture } from '@/redux/map/map.fixtures'; -import { AppDispatch, RootState, StoreType } from '@/redux/store'; -import { getReduxStoreWithActionsListener } from '@/utils/testing/getReduxStoreActionsListener'; +import { StoreType } from '@/redux/store'; import { InitialStoreState, getReduxWrapperWithStore, } from '@/utils/testing/getReduxWrapperWithStore'; import { render, screen } from '@testing-library/react'; -import { MockStoreEnhanced } from 'redux-mock-store'; -import { PROJECT_STATE_INITIAL_MOCK } from '@/redux/project/project.mock'; -import { projectFixture } from '@/models/fixtures/projectFixture'; import { MapAdditionalOptions } from './MapAdditionalOptions.component'; const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => { @@ -31,71 +21,9 @@ const renderComponent = (initialStoreState: InitialStoreState = {}): { store: St ); }; -const renderComponentWithActionListener = ( - initialStoreState: InitialStoreState = {}, -): { store: MockStoreEnhanced<Partial<RootState>, AppDispatch> } => { - const { Wrapper, store } = getReduxStoreWithActionsListener(initialStoreState); - - return ( - render( - <Wrapper> - <MapAdditionalOptions /> - </Wrapper>, - ), - { - store, - } - ); -}; - describe('MapAdditionalOptions - component', () => { it('should display background selector', () => { renderComponent(); expect(screen.getByTestId('background-selector')).toBeInTheDocument(); }); - - it('should render browse overview images button', () => { - renderComponent(); - expect(screen.getByText('Browse overview images')).toBeInTheDocument(); - }); - - it('should open overview image modal on button click', () => { - const { store } = renderComponentWithActionListener({ - project: { - ...PROJECT_STATE_INITIAL_MOCK, - data: projectFixture, - }, - map: initialMapStateFixture, - backgrounds: { ...BACKGROUND_INITIAL_STATE_MOCK, data: BACKGROUNDS_MOCK }, - }); - - const overviewImageButton = screen.getByText('Browse overview images'); - overviewImageButton.click(); - - const actions = store.getActions(); - expect(actions[FIRST_ARRAY_ELEMENT]).toStrictEqual({ - payload: projectFixture.topOverviewImage?.idObject, - type: 'modal/openOverviewImagesModalById', - }); - }); - it('should disable button browse overview images if there are no overview images', () => { - const { store } = renderComponentWithActionListener({ - project: { - ...PROJECT_STATE_INITIAL_MOCK, - data: { - ...projectFixture, - overviewImageViews: [], - }, - }, - map: initialMapStateFixture, - backgrounds: { ...BACKGROUND_INITIAL_STATE_MOCK, data: BACKGROUNDS_MOCK }, - }); - - const overviewImageButton = screen.getByText('Browse overview images'); - expect(overviewImageButton).toBeDisabled(); - overviewImageButton.click(); - - const actions = store.getActions(); - expect(actions).toStrictEqual([]); - }); }); diff --git a/src/components/Map/MapAdditionalOptions/MapAdditionalOptions.component.tsx b/src/components/Map/MapAdditionalOptions/MapAdditionalOptions.component.tsx index a06ecbdb7e999f2127996e28edafd20d4baeed66..ce96a90761bdf9ee6d5600b68d64268e4374225a 100644 --- a/src/components/Map/MapAdditionalOptions/MapAdditionalOptions.component.tsx +++ b/src/components/Map/MapAdditionalOptions/MapAdditionalOptions.component.tsx @@ -1,37 +1,10 @@ -import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; -import { openOverviewImagesModalById } from '@/redux/modal/modal.slice'; -import { - projectDefaultOverviewImageIdSelector, - projectOverviewImagesSelector, -} from '@/redux/project/project.selectors'; -import { Button } from '@/shared/Button'; -import { useSelector } from 'react-redux'; import { twMerge } from 'tailwind-merge'; -import { ZERO } from '@/constants/common'; import { BackgroundSelector } from './BackgroundsSelector'; // top-[calc(64px+40px+24px)] -> TOP_BAR_HEIGHT+MAP_NAVIGATION_HEIGHT+DISTANCE_FROM_MAP_NAVIGATION -export const MapAdditionalOptions = (): JSX.Element => { - const dispatch = useAppDispatch(); - const defaultOverviewImageId = useSelector(projectDefaultOverviewImageIdSelector); - const overviewImages = useSelector(projectOverviewImagesSelector); - const overviewImagesEmpty = overviewImages.length === ZERO; - - const handleBrowseOverviewImagesClick = (): void => { - dispatch(openOverviewImagesModalById(defaultOverviewImageId)); - }; - - return ( - <div className={twMerge('absolute right-6 top-[calc(64px+40px+24px)] z-10 flex')}> - <Button - className="mr-4" - onClick={handleBrowseOverviewImagesClick} - disabled={overviewImagesEmpty} - > - Browse overview images - </Button> - <BackgroundSelector /> - </div> - ); -}; +export const MapAdditionalOptions = (): JSX.Element => ( + <div className={twMerge('absolute right-6 top-[calc(64px+40px+24px)] z-10 flex')}> + <BackgroundSelector /> + </div> +); diff --git a/src/redux/backgrounds/background.selectors.ts b/src/redux/backgrounds/background.selectors.ts index b815cf59b879e5cf3460cbeb092bef85612ae065..eda3703446dc38a0ca3aa4997b99296f1e48cba9 100644 --- a/src/redux/backgrounds/background.selectors.ts +++ b/src/redux/backgrounds/background.selectors.ts @@ -2,11 +2,21 @@ import { EMPTY_BACKGROUND_NAME } from '@/constants/backgrounds'; import { createSelector } from '@reduxjs/toolkit'; import { mapDataSelector } from '../map/map.selectors'; import { rootSelector } from '../root/root.selectors'; +import { PATHWAYS_AND_COMPARTMENTS_BACKGROUND, SEMANTIC_BACKGROUND } from './backgrounds.constants'; export const backgroundsSelector = createSelector(rootSelector, state => state.backgrounds); export const backgroundsDataSelector = createSelector(backgroundsSelector, backgrounds => { - return backgrounds?.data || []; + return (backgrounds?.data || []).map(background => { + if (background.name === PATHWAYS_AND_COMPARTMENTS_BACKGROUND) { + return { + ...background, + name: SEMANTIC_BACKGROUND, + }; + } + + return background; + }); }); const MAIN_BACKGROUND = 0; diff --git a/src/redux/backgrounds/backgrounds.constants.ts b/src/redux/backgrounds/backgrounds.constants.ts index 0fa103f27a2b1c77b8521798570f223a6ac4f6d0..49499431e770bfcc5f57a0678931b71475a346eb 100644 --- a/src/redux/backgrounds/backgrounds.constants.ts +++ b/src/redux/backgrounds/backgrounds.constants.ts @@ -1 +1,3 @@ export const BACKGROUNDS_FETCHING_ERROR_PREFIX = 'Failed to fetch backgrounds'; +export const PATHWAYS_AND_COMPARTMENTS_BACKGROUND = 'Pathways and compartments'; +export const SEMANTIC_BACKGROUND = 'Semantic';