Skip to content
Snippets Groups Projects
Commit fb32acbe authored by Adrian Orłów's avatar Adrian Orłów
Browse files

Merge remote-tracking branch 'origin/development' into feature/map-data-mgmt

parents c3fc37dc a5230e3f
No related branches found
No related tags found
2 merge requests!223reset the pin numbers before search results are fetch (so the results will be...,!35feat(map): add data management
Pipeline #79932 passed
Showing
with 469 additions and 97 deletions
<svg width="18" height="24" viewBox="0 0 18 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 0C4.575 0 0 3.375 0 9C0 14.325 8.1 22.65 8.475 23.025C8.625 23.175 8.775 23.25 9 23.25C9.225 23.25 9.375 23.175 9.525 23.025C9.9 22.65 18 14.4 18 9C18 3.375 13.425 0 9 0ZM9 12C7.35 12 6 10.65 6 9C6 7.35 7.35 6 9 6C10.65 6 12 7.35 12 9C12 10.65 10.65 12 9 12Z" fill="#E17221"/>
<circle cx="9.0002" cy="8.99922" r="4.8" fill="#E17221"/>
</svg>
......@@ -22,6 +22,7 @@
"next": "13.4.19",
"ol": "^8.1.0",
"postcss": "8.4.29",
"query-string": "7.1.3",
"react": "18.2.0",
"react-accessible-accordion": "^5.0.0",
"react-dom": "18.2.0",
......@@ -60,6 +61,7 @@
"jest-junit": "^16.0.0",
"jest-watch-typeahead": "^2.2.2",
"lint-staged": "^14.0.1",
"next-router-mock": "^0.9.10",
"prettier": "^3.0.3",
"prettier-2": "npm:prettier@^2",
"prettier-plugin-tailwindcss": "^0.5.6",
......@@ -4320,6 +4322,14 @@
"integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==",
"dev": true
},
"node_modules/decode-uri-component": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
"engines": {
"node": ">=0.10"
}
},
"node_modules/dedent": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
......@@ -5828,6 +5838,14 @@
"node": ">=8"
}
},
"node_modules/filter-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
"integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/find-node-modules": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/find-node-modules/-/find-node-modules-2.1.3.tgz",
......@@ -9494,6 +9512,16 @@
}
}
},
"node_modules/next-router-mock": {
"version": "0.9.10",
"resolved": "https://registry.npmjs.org/next-router-mock/-/next-router-mock-0.9.10.tgz",
"integrity": "sha512-bK6sRb/xGNFgHVUZuvuApn6KJBAKTPiP36A7a9mO77U4xQO5ukJx9WHlU67Tv8AuySd09pk0+Hu8qMVIAmLO6A==",
"dev": true,
"peerDependencies": {
"next": ">=10.0.0",
"react": ">=17.0.0"
}
},
"node_modules/next/node_modules/postcss": {
"version": "8.4.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
......@@ -10467,6 +10495,23 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/query-string": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz",
"integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==",
"dependencies": {
"decode-uri-component": "^0.2.2",
"filter-obj": "^1.1.0",
"split-on-first": "^1.0.0",
"strict-uri-encode": "^2.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/querystringify": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
......@@ -11316,6 +11361,14 @@
"integrity": "sha512-lpT8hSQp9jAKp9mhtBU4Xjon8LPGBvLIuBiSVhMEtmLecTh2mO0tlqrAMp47tBXzMr13NJMQ2lf7RpQGLJ3HsQ==",
"dev": true
},
"node_modules/split-on-first": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
"integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==",
"engines": {
"node": ">=6"
}
},
"node_modules/split2": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
......@@ -11397,6 +11450,14 @@
"node": ">=10.0.0"
}
},
"node_modules/strict-uri-encode": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
"integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==",
"engines": {
"node": ">=4"
}
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
......
import { getBioEntityContents } from '@/redux/bioEntityContents/bioEntityContents.thunks';
import { getChemicals } from '@/redux/chemicals/chemicals.thunks';
import { getDrugs } from '@/redux/drugs/drugs.thunks';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { getMirnas } from '@/redux/mirnas/mirnas.thunks';
import { selectSearchValue } from '@/redux/search/search.selectors';
import { setSearchValue } from '@/redux/search/search.slice';
import { useSelector } from 'react-redux';
const ReduxPage = (): JSX.Element => {
const dispatch = useAppDispatch();
const searchValue = useSelector(selectSearchValue);
const triggerSyncUpdate = (): void => {
// eslint-disable-next-line prefer-template
const newValue = searchValue + 'test';
dispatch(setSearchValue(newValue));
dispatch(getDrugs('aspirin'));
dispatch(getMirnas('hsa-miR-302b-3p'));
dispatch(getBioEntityContents('park7'));
dispatch(getChemicals('Corticosterone'));
};
return (
<div>
{searchValue}
<button type="button" onClick={triggerSyncUpdate}>
sync update
</button>
</div>
);
};
export default ReduxPage;
import '@testing-library/jest-dom';
jest.mock('next/router', () => require('next-router-mock'));
<svg width="18" height="24" viewBox="0 0 18 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 0C4.575 0 0 3.375 0 9C0 14.325 8.1 22.65 8.475 23.025C8.625 23.175 8.775 23.25 9 23.25C9.225 23.25 9.375 23.175 9.525 23.025C9.9 22.65 18 14.4 18 9C18 3.375 13.425 0 9 0ZM9 12C7.35 12 6 10.65 6 9C6 7.35 7.35 6 9 6C10.65 6 12 7.35 12 9C12 10.65 10.65 12 9 12Z" fill="#E17221"/>
<circle cx="9.0002" cy="8.99922" r="4.8" fill="#E17221"/>
<path d="M8.846 15V7.344L7.22 8.322V7.332L8.846 6.36H9.74V15H8.846Z" fill="white"/>
</svg>
import { RenderResult, screen } from '@testing-library/react';
import { renderComponentWithProvider } from '@/utils/renderComponentWithProvider';
import drawerReducer from '@/redux/drawer/drawer.slice';
import type { DrawerState } from '@/redux/drawer/drawer.types';
import { ToolkitStoreWithSingleSlice } from '@/utils/createStoreInstanceUsingSliceReducer';
import { getReduxWrapperUsingSliceReducer } from '@/utils/testing/getReduxWrapperUsingSliceReducer';
import { render, screen } from '@testing-library/react';
import { NavBar } from './NavBar.component';
const renderComponent = (): RenderResult => renderComponentWithProvider(<NavBar />);
const renderComponent = (): { store: ToolkitStoreWithSingleSlice<DrawerState> } => {
const { Wrapper, store } = getReduxWrapperUsingSliceReducer('drawer', drawerReducer);
return (
render(
<Wrapper>
<NavBar />
</Wrapper>,
),
{
store,
}
);
};
describe('NavBar - component', () => {
it('Should contain navigation buttons and logos with powered by info', () => {
......
import Image from 'next/image';
import { IconButton } from '@/shared/IconButton';
import logoImg from '@/assets/images/logo.png';
import luxembourgLogoImg from '@/assets/images/luxembourg-logo.png';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { openDrawer } from '@/redux/drawer/drawer.slice';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { IconButton } from '@/shared/IconButton';
import Image from 'next/image';
export const NavBar = (): JSX.Element => {
const dispatch = useAppDispatch();
......
import { screen, render, RenderResult, fireEvent } from '@testing-library/react';
import { StoreType } from '@/redux/store';
import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
import { fireEvent, render, screen } from '@testing-library/react';
import mockedRouter from 'next-router-mock';
import { SearchBar } from './SearchBar.component';
const renderComponent = (): RenderResult => render(<SearchBar />);
const renderComponent = (): { store: StoreType } => {
const { Wrapper, store } = getReduxWrapperWithStore();
return (
render(
<Wrapper>
<SearchBar />
</Wrapper>,
),
{
store,
}
);
};
describe('SearchBar - component', () => {
it('should let user type text', () => {
renderComponent();
const input = screen.getByTestId<HTMLInputElement>('search-input');
const input = screen.getByTestId('search-input');
fireEvent.change(input, { target: { value: 'test value' } });
expect(screen.getByDisplayValue('test value')).toBeInTheDocument();
expect(input.value).toBe('test value');
});
it('should disable button when the user clicks the lens button', () => {
renderComponent();
const input = screen.getByTestId<HTMLInputElement>('search-input');
fireEvent.change(input, { target: { value: 'park7' } });
const button = screen.getByRole('button');
fireEvent.click(button);
expect(button).toBeDisabled();
});
it('should disable input when the user clicks the Enter', () => {
renderComponent();
const input = screen.getByTestId<HTMLInputElement>('search-input');
fireEvent.change(input, { target: { value: 'park7' } });
fireEvent.keyDown(input, { key: 'Enter', code: 'Enter', charCode: 13 });
expect(input).toBeDisabled();
});
it('should set parameters on the url when the user enters a value in the search bar and clicks Enter', () => {
renderComponent();
const input = screen.getByTestId<HTMLInputElement>('search-input');
fireEvent.change(input, { target: { value: 'park7' } });
const button = screen.getByRole('button');
fireEvent.click(button);
expect(button).toBeDisabled();
expect(mockedRouter).toMatchObject({
asPath: '/?search=park7',
pathname: '/',
query: { search: 'park7' },
});
});
it('should set parameters on the url when the user enters a value in the search bar and clicks lens button', () => {
renderComponent();
const input = screen.getByTestId<HTMLInputElement>('search-input');
fireEvent.change(input, { target: { value: 'park7' } });
fireEvent.keyDown(input, { key: 'Enter', code: 'Enter', charCode: 13 });
expect(input).toBeDisabled();
expect(mockedRouter).toMatchObject({
asPath: '/?search=park7',
pathname: '/',
query: { search: 'park7' },
});
});
it('should set the value on the input filed when the user has query parameters in the url', () => {
renderComponent();
mockedRouter.push('/?search=park7');
const input = screen.getByTestId<HTMLInputElement>('search-input');
expect(input.value).toBe('park7');
expect(mockedRouter).toMatchObject({
asPath: '/?search=park7',
pathname: '/',
query: { search: 'park7' },
});
});
});
import Image from 'next/image';
import { ChangeEvent, useState } from 'react';
import lensIcon from '@/assets/vectors/icons/lens.svg';
import { useParamsQuery } from '@/components/FunctionalArea/TopBar/SearchBar/hooks/useParamsQuery';
import { isDrawerOpenSelector } from '@/redux/drawer/drawer.selectors';
import { clearSearchDrawerState, openDrawer } from '@/redux/drawer/drawer.slice';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import {
isPendingSearchStatusSelector,
searchValueSelector,
} from '@/redux/search/search.selectors';
import { getSearchData } from '@/redux/search/search.thunks';
import Image from 'next/image';
import { ChangeEvent, KeyboardEvent, useState } from 'react';
import { useSelector } from 'react-redux';
const ENTER_KEY_CODE = 'Enter';
export const SearchBar = (): JSX.Element => {
const [searchValue, setSearchValue] = useState<string>('');
const isPendingSearchStatus = useSelector(isPendingSearchStatusSelector);
const prevSearchValue = useSelector(searchValueSelector);
const isDrawerOpen = useSelector(isDrawerOpenSelector);
const { setSearchQueryInRouter, searchParams } = useParamsQuery();
const [searchValue, setSearchValue] = useState<string>((searchParams?.search as string) || '');
const dispatch = useAppDispatch();
const isSameSearchValue = prevSearchValue === searchValue;
const onSearchChange = (event: ChangeEvent<HTMLInputElement>): void => {
const openSearchDrawerIfClosed = (): void => {
if (!isDrawerOpen) {
dispatch(openDrawer('search'));
dispatch(clearSearchDrawerState());
}
};
const onSearchChange = (event: ChangeEvent<HTMLInputElement>): void =>
setSearchValue(event.target.value);
const onSearchClick = (): void => {
if (!isSameSearchValue) {
dispatch(getSearchData(searchValue));
setSearchQueryInRouter(searchValue);
openSearchDrawerIfClosed();
}
};
const handleKeyPress = (event: KeyboardEvent<HTMLInputElement>): void => {
if (!isSameSearchValue && event.code === ENTER_KEY_CODE) {
dispatch(getSearchData(searchValue));
setSearchQueryInRouter(searchValue);
openSearchDrawerIfClosed();
}
};
return (
......@@ -16,16 +59,25 @@ export const SearchBar = (): JSX.Element => {
name="search-input"
aria-label="search-input"
data-testid="search-input"
onKeyDown={handleKeyPress}
onChange={onSearchChange}
disabled={isPendingSearchStatus}
className="h-9 w-72 rounded-[64px] border border-transparent bg-cultured px-4 py-2.5 text-xs font-medium text-font-400 outline-none hover:border-greyscale-600 focus:border-greyscale-600"
/>
<Image
src={lensIcon}
alt="lens icon"
height={16}
width={16}
className="absolute right-4 top-2.5"
/>
<button
disabled={isPendingSearchStatus}
type="button"
className="bg-transparent"
onClick={onSearchClick}
>
<Image
src={lensIcon}
alt="lens icon"
height={16}
width={16}
className="absolute right-4 top-2.5"
/>
</button>
</div>
);
};
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { getSearchData } from '@/redux/search/search.thunks';
import { useRouter } from 'next/router';
import type { ParsedQuery } from 'query-string';
import qs from 'query-string';
import { useEffect } from 'react';
type UseParamsQuery = {
setSearchQueryInRouter: (searchValue: string) => void;
searchParams: ParsedQuery<string>;
};
export const useParamsQuery = (): UseParamsQuery => {
const router = useRouter();
const dispatch = useAppDispatch();
const path = router.asPath;
// The number of the character from which to cut the characters from path.
const sliceFromCharNumber = 2;
// The number of the character at which to end the cut string from path.
const sliceToCharNumber = path.length;
const searchParams = qs.parse(path.slice(sliceFromCharNumber, sliceToCharNumber));
const setSearchQueryInRouter = (searchValue: string): void => {
const searchQuery = {
search: searchValue,
};
router.push(`?${qs.stringify(searchQuery)}`);
};
useEffect(() => {
if (searchParams?.search) dispatch(getSearchData(searchParams.search as string));
}, [dispatch]);
return { setSearchQueryInRouter, searchParams };
};
import { screen, render, RenderResult } from '@testing-library/react';
import { StoreType } from '@/redux/store';
import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
import { render, screen } from '@testing-library/react';
import { TopBar } from './TopBar.component';
const renderComponent = (): RenderResult => render(<TopBar />);
const renderComponent = (): { store: StoreType } => {
const { Wrapper, store } = getReduxWrapperWithStore();
return (
render(
<Wrapper>
<TopBar />
</Wrapper>,
),
{
store,
}
);
};
describe('TopBar - component', () => {
it('Should contain user avatar, search bar', () => {
......
import { ToolkitStoreWithSingleSlice } from '@/utils/createStoreInstanceUsingSliceReducer';
import { DrawerState } from '@/redux/drawer/drawer.types';
import { getReduxWrapperUsingSliceReducer } from '@/utils/testing/getReduxWrapperUsingSliceReducer';
import { screen, render, act, fireEvent } from '@testing-library/react';
import drawerReducer, { openDrawer } from '@/redux/drawer/drawer.slice';
import { openDrawer } from '@/redux/drawer/drawer.slice';
import { StoreType } from '@/redux/store';
import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
import { act, fireEvent, render, screen } from '@testing-library/react';
import { Drawer } from './Drawer.component';
const renderComponent = (): { store: ToolkitStoreWithSingleSlice<DrawerState> } => {
const { Wrapper, store } = getReduxWrapperUsingSliceReducer('drawer', drawerReducer);
const renderComponent = (): { store: StoreType } => {
const { Wrapper, store } = getReduxWrapperWithStore();
return (
render(
<Wrapper>
......
import { DRAWER_ROLE } from '@/components/Map/Drawer/Drawer.constants';
import { drawerSelector } from '@/redux/drawer/drawer.selectors';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import dynamic from 'next/dynamic';
import { twMerge } from 'tailwind-merge';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { drawerDataSelector } from '@/redux/drawer/drawer.selectors';
import { DRAWER_ROLE } from '@/components/Map/Drawer/Drawer.constants';
const SearchDrawerContent = dynamic(
async () =>
......@@ -15,17 +15,17 @@ const SearchDrawerContent = dynamic(
);
export const Drawer = (): JSX.Element => {
const { open, drawerName } = useAppSelector(drawerDataSelector);
const { isOpen, drawerName } = useAppSelector(drawerSelector);
return (
<div
className={twMerge(
'absolute left-[88px] top-[104px] z-10 h-calc-drawer w-[432px] -translate-x-full transform bg-white-pearl text-font-500 transition-all duration-500',
open && 'translate-x-0',
isOpen && 'translate-x-0',
)}
role={DRAWER_ROLE}
>
{open && drawerName === 'search' && <SearchDrawerContent />}
{isOpen && drawerName === 'search' && <SearchDrawerContent />}
{/* other drawers comes here, should use dynamic import */}
</div>
);
......
import {
Accordion,
AccordionItem,
AccordionItemButton,
AccordionItemPanel,
......@@ -11,30 +10,28 @@ import { BioEntitiesSubmapItem } from '@/components/Map/Drawer/SearchDrawerConte
export const BioEntitiesAccordion = (): JSX.Element => {
const entity = { mapName: 'main map', numberOfEntities: 21 };
return (
<Accordion allowZeroExpanded>
<AccordionItem>
<AccordionItemHeading>
<AccordionItemButton>Content (2137)</AccordionItemButton>
</AccordionItemHeading>
<AccordionItemPanel className="">
<BioEntitiesSubmapItem
mapName={entity.mapName}
numberOfEntities={entity.numberOfEntities}
/>
<BioEntitiesSubmapItem
mapName={entity.mapName}
numberOfEntities={entity.numberOfEntities}
/>
<BioEntitiesSubmapItem
mapName={entity.mapName}
numberOfEntities={entity.numberOfEntities}
/>
<BioEntitiesSubmapItem
mapName={entity.mapName}
numberOfEntities={entity.numberOfEntities}
/>
</AccordionItemPanel>
</AccordionItem>
</Accordion>
<AccordionItem>
<AccordionItemHeading>
<AccordionItemButton>Content (2137)</AccordionItemButton>
</AccordionItemHeading>
<AccordionItemPanel className="">
<BioEntitiesSubmapItem
mapName={entity.mapName}
numberOfEntities={entity.numberOfEntities}
/>
<BioEntitiesSubmapItem
mapName={entity.mapName}
numberOfEntities={entity.numberOfEntities}
/>
<BioEntitiesSubmapItem
mapName={entity.mapName}
numberOfEntities={entity.numberOfEntities}
/>
<BioEntitiesSubmapItem
mapName={entity.mapName}
numberOfEntities={entity.numberOfEntities}
/>
</AccordionItemPanel>
</AccordionItem>
);
};
import { render, screen } from '@testing-library/react';
import { StoreType } from '@/redux/store';
import {
InitialStoreState,
getReduxWrapperWithStore,
} from '@/utils/testing/getReduxWrapperWithStore';
import { chemicalsFixture } from '@/models/fixtures/chemicalsFixture';
import { Accordion } from '@/shared/Accordion';
import { ChemicalsAccordion } from './ChemicalsAccordion.component';
const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState);
return (
render(
<Wrapper>
<Accordion>
<ChemicalsAccordion />
</Accordion>
</Wrapper>,
),
{
store,
}
);
};
describe('DrugsAccordion - component', () => {
it('should display drugs number after succesfull chemicals search', () => {
renderComponent({
chemicals: { data: chemicalsFixture, loading: 'succeeded', error: { name: '', message: '' } },
});
expect(screen.getByText('Chemicals (2)')).toBeInTheDocument();
});
it('should display loading indicator while waiting for chemicals search response', () => {
renderComponent({
chemicals: { data: [], loading: 'pending', error: { name: '', message: '' } },
});
expect(screen.getByText('Chemicals (Loading...)')).toBeInTheDocument();
});
});
import {
numberOfChemicalsSelector,
loadingChemicalsStatusSelector,
} from '@/redux/chemicals/chemicals.selectors';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { AccordionItem, AccordionItemHeading, AccordionItemButton } from '@/shared/Accordion';
export const ChemicalsAccordion = (): JSX.Element => {
const chemicalsNumber = useAppSelector(numberOfChemicalsSelector);
const drugsState = useAppSelector(loadingChemicalsStatusSelector);
return (
<AccordionItem>
<AccordionItemHeading>
<AccordionItemButton variant="non-expandable">
Chemicals
{drugsState === 'pending' && ' (Loading...)'}
{drugsState === 'succeeded' && ` (${chemicalsNumber})`}
</AccordionItemButton>
</AccordionItemHeading>
</AccordionItem>
);
};
export { ChemicalsAccordion } from './ChemicalsAccordion.component';
import { drugsFixture } from '@/models/fixtures/drugFixtures';
import { StoreType } from '@/redux/store';
import { Accordion } from '@/shared/Accordion';
import {
InitialStoreState,
getReduxWrapperWithStore,
} from '@/utils/testing/getReduxWrapperWithStore';
import { render, screen } from '@testing-library/react';
import { DrugsAccordion } from './DrugsAccordion.component';
const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState);
return (
render(
<Wrapper>
<Accordion>
<DrugsAccordion />
</Accordion>
</Wrapper>,
),
{
store,
}
);
};
describe('DrugsAccordion - component', () => {
it('should display drugs number after succesfull drug search', () => {
renderComponent({
drugs: { data: drugsFixture, loading: 'succeeded', error: { name: '', message: '' } },
});
expect(screen.getByText('Drugs (2)')).toBeInTheDocument();
});
it('should display loading indicator while waiting for drug search response', () => {
renderComponent({
drugs: { data: [], loading: 'pending', error: { name: '', message: '' } },
});
expect(screen.getByText('Drugs (Loading...)')).toBeInTheDocument();
});
});
import { loadingDrugsStatusSelector, numberOfDrugsSelector } from '@/redux/drugs/drugs.selectors';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { AccordionItem, AccordionItemHeading, AccordionItemButton } from '@/shared/Accordion';
export const DrugsAccordion = (): JSX.Element => {
const drugsNumber = useAppSelector(numberOfDrugsSelector);
const drugsState = useAppSelector(loadingDrugsStatusSelector);
return (
<AccordionItem>
<AccordionItemHeading>
<AccordionItemButton variant="non-expandable">
Drugs
{drugsState === 'pending' && ' (Loading...)'}
{drugsState === 'succeeded' && ` (${drugsNumber})`}
</AccordionItemButton>
</AccordionItemHeading>
</AccordionItem>
);
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment