diff --git a/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.test.tsx b/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.test.tsx index f5ac55b046d8c3ab4aac649601a79ca602e42c35..a8c934f4cdc75817e2fd45ff6bdbd5dadb301fc5 100644 --- a/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.test.tsx +++ b/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.test.tsx @@ -1,12 +1,10 @@ -import searchReducer from '@/redux/search/search.slice'; -import type { SearchState } from '@/redux/search/search.types'; -import { ToolkitStoreWithSingleSlice } from '@/utils/createStoreInstanceUsingSliceReducer'; -import { getReduxWrapperUsingSliceReducer } from '@/utils/testing/getReduxWrapperUsingSliceReducer'; +import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore'; +import { StoreType } from '@/redux/store'; import { fireEvent, render, screen } from '@testing-library/react'; import { SearchBar } from './SearchBar.component'; -const renderComponent = (): { store: ToolkitStoreWithSingleSlice<SearchState> } => { - const { Wrapper, store } = getReduxWrapperUsingSliceReducer('search', searchReducer); +const renderComponent = (): { store: StoreType } => { + const { Wrapper, store } = getReduxWrapperWithStore(); return ( render( @@ -23,8 +21,8 @@ const renderComponent = (): { store: ToolkitStoreWithSingleSlice<SearchState> } describe('SearchBar - component', () => { it('should let user type text', () => { renderComponent(); - const input = screen.getByTestId<HTMLInputElement>('search-input'); + fireEvent.change(input, { target: { value: 'test value' } }); expect(input.value).toBe('test value'); @@ -32,13 +30,12 @@ describe('SearchBar - component', () => { 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' } }); - expect(input.value).toBe('park7'); + fireEvent.change(input, { target: { value: 'park7' } }); const button = screen.getByRole('button'); + fireEvent.click(button); expect(button).toBeDisabled(); @@ -46,12 +43,9 @@ describe('SearchBar - component', () => { it('should disable input when the user clicks the Enter', () => { renderComponent(); - const input = screen.getByTestId<HTMLInputElement>('search-input'); - fireEvent.change(input, { target: { value: 'park7' } }); - - expect(input.value).toBe('park7'); + fireEvent.change(input, { target: { value: 'park7' } }); fireEvent.keyDown(input, { key: 'Enter', code: 'Enter', charCode: 13 }); expect(input).toBeDisabled(); diff --git a/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx b/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx index 1932b5a2e240252e93ae09000b4fe94a5c5b4539..77ec065c9809b2e9355bd9a2bdbc1ccc9d944aba 100644 --- a/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx +++ b/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx @@ -1,4 +1,6 @@ import lensIcon from '@/assets/vectors/icons/lens.svg'; +import { isDrawerOpenSelector } from '@/redux/drawer/drawer.selectors'; +import { openDrawer } from '@/redux/drawer/drawer.slice'; import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; import { isPendingSearchStatusSelector, @@ -16,17 +18,31 @@ export const SearchBar = (): JSX.Element => { const dispatch = useAppDispatch(); const isPendingSearchStatus = useSelector(isPendingSearchStatusSelector); const prevSearchValue = useSelector(searchValueSelector); + const isDrawerOpen = useSelector(isDrawerOpenSelector); const isSameSearchValue = prevSearchValue === searchValue; + const openSearchDrawerIfClosed = (): void => { + if (!isDrawerOpen) { + dispatch(openDrawer('search')); + } + }; + const onSearchChange = (event: ChangeEvent<HTMLInputElement>): void => setSearchValue(event.target.value); - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - const onSearchClick = () => !isSameSearchValue && dispatch(getSearchData(searchValue)); + const onSearchClick = (): void => { + if (!isSameSearchValue) { + dispatch(getSearchData(searchValue)); + openSearchDrawerIfClosed(); + } + }; const handleKeyPress = (event: KeyboardEvent<HTMLInputElement>): void => { - if (!isSameSearchValue && event.code === ENTER_KEY_CODE) dispatch(getSearchData(searchValue)); + if (!isSameSearchValue && event.code === ENTER_KEY_CODE) { + dispatch(getSearchData(searchValue)); + openSearchDrawerIfClosed(); + } }; return ( diff --git a/src/components/FunctionalArea/TopBar/TopBar.component.test.tsx b/src/components/FunctionalArea/TopBar/TopBar.component.test.tsx index 6ff1d8495eaa23d3d947a52f707b6e03a3ba85ac..e6e44ac13532f839d4b82fbde10dfdd537fc49ce 100644 --- a/src/components/FunctionalArea/TopBar/TopBar.component.test.tsx +++ b/src/components/FunctionalArea/TopBar/TopBar.component.test.tsx @@ -1,12 +1,10 @@ -import searchReducer from '@/redux/search/search.slice'; -import type { SearchState } from '@/redux/search/search.types'; -import { ToolkitStoreWithSingleSlice } from '@/utils/createStoreInstanceUsingSliceReducer'; -import { getReduxWrapperUsingSliceReducer } from '@/utils/testing/getReduxWrapperUsingSliceReducer'; +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 = (): { store: ToolkitStoreWithSingleSlice<SearchState> } => { - const { Wrapper, store } = getReduxWrapperUsingSliceReducer('search', searchReducer); +const renderComponent = (): { store: StoreType } => { + const { Wrapper, store } = getReduxWrapperWithStore(); return ( render( diff --git a/src/components/Map/Drawer/Drawer.component.test.tsx b/src/components/Map/Drawer/Drawer.component.test.tsx index a0663188edef546d4eb44d1fde6591bbcdbaa1f5..20c5630bdf1f7b24cb79fdadf1686adbf3c5c7ac 100644 --- a/src/components/Map/Drawer/Drawer.component.test.tsx +++ b/src/components/Map/Drawer/Drawer.component.test.tsx @@ -1,13 +1,11 @@ -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 { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore'; +import { StoreType } from '@/redux/store'; 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> diff --git a/src/components/Map/Drawer/Drawer.component.tsx b/src/components/Map/Drawer/Drawer.component.tsx index 4e692262a8a87a4e45a2a079b6db8c3551b4b4af..a08fd813d0a4d58d96ae20d5fce40d7d84212671 100644 --- a/src/components/Map/Drawer/Drawer.component.tsx +++ b/src/components/Map/Drawer/Drawer.component.tsx @@ -1,8 +1,8 @@ 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'; +import { drawerSelector } from '@/redux/drawer/drawer.selectors'; 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> ); diff --git a/src/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/BioEntitiesAccordion.component.tsx b/src/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/BioEntitiesAccordion.component.tsx index ff006e6b0d2f9cc5ba046e81ec909ac018bf7995..6442eca6bb1de817ab1ee0dfdf8c343b5c24edea 100644 --- a/src/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/BioEntitiesAccordion.component.tsx +++ b/src/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/BioEntitiesAccordion.component.tsx @@ -1,5 +1,4 @@ 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> ); }; diff --git a/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/DrugsAccordion.component.test.tsx b/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/DrugsAccordion.component.test.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5abedd8f8e3d8b423068b621cb4e41a9a71a4f3d --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/DrugsAccordion.component.test.tsx @@ -0,0 +1,41 @@ +import { render, screen } from '@testing-library/react'; +import { StoreType } from '@/redux/store'; +import { + InitialStoreState, + getReduxWrapperWithStore, +} from '@/utils/testing/getReduxWrapperWithStore'; +import { drugsFixture } from '@/models/fixtures/drugFixtures'; +import { Accordion } from '@/shared/Accordion'; +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(); + }); +}); diff --git a/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/DrugsAccordion.component.tsx b/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/DrugsAccordion.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..af14387558dbfecd9b62eef326603c17dd49743c --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/DrugsAccordion.component.tsx @@ -0,0 +1,20 @@ +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> + ); +}; diff --git a/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/index.ts b/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..36be6a7acb3a6c9fb32efdfa5cbb70efac87a909 --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/index.ts @@ -0,0 +1 @@ +export { DrugsAccordion } from './DrugsAccordion.component'; diff --git a/src/components/Map/Drawer/SearchDrawerContent/SearchDrawerContent.component.tsx b/src/components/Map/Drawer/SearchDrawerContent/SearchDrawerContent.component.tsx index ad1b350ce514b1077ec72147ee250400c3c1174f..ea53111ce0ea851cfee87d48aeb9fbeb075ed64b 100644 --- a/src/components/Map/Drawer/SearchDrawerContent/SearchDrawerContent.component.tsx +++ b/src/components/Map/Drawer/SearchDrawerContent/SearchDrawerContent.component.tsx @@ -1,7 +1,9 @@ import { BioEntitiesAccordion } from '@/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion'; +import { DrugsAccordion } from '@/components/Map/Drawer/SearchDrawerContent/DrugsAccordion'; import { closeDrawer } from '@/redux/drawer/drawer.slice'; import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; import { IconButton } from '@/shared/IconButton'; +import { Accordion } from '@/shared/Accordion'; export const CLOSE_BUTTON_ROLE = 'close-drawer-button'; @@ -28,7 +30,10 @@ export const SearchDrawerContent = (): JSX.Element => { /> </div> <div className="px-6"> - <BioEntitiesAccordion /> + <Accordion allowZeroExpanded> + <BioEntitiesAccordion /> + <DrugsAccordion /> + </Accordion> </div> </div> ); diff --git a/src/redux/drawer/drawer.reducers.test.ts b/src/redux/drawer/drawer.reducers.test.ts index 7701a180c2bc0c148b050e96c0ffc2bc523f1bcd..c9aa33dbd506bcedb60cb6ab56180057ac44c92e 100644 --- a/src/redux/drawer/drawer.reducers.test.ts +++ b/src/redux/drawer/drawer.reducers.test.ts @@ -5,7 +5,7 @@ import drawerReducer, { openDrawer, closeDrawer } from './drawer.slice'; import type { DrawerState } from './drawer.types'; const INITIAL_STATE: DrawerState = { - open: false, + isOpen: false, drawerName: 'none', }; @@ -38,19 +38,19 @@ describe('drawer reducer', () => { it('should update the store when you click a project info button on the nav bar', async () => { const { type } = await store.dispatch(openDrawer('project-info')); - const { open, drawerName } = store.getState().drawer; + const { isOpen, drawerName } = store.getState().drawer; expect(type).toBe('drawer/openDrawer'); - expect(open).toBe(true); + expect(isOpen).toBe(true); expect(drawerName).toEqual('project-info'); }); it('should update the store when you click the close button on the drawer', async () => { const { type } = await store.dispatch(closeDrawer()); - const { open, drawerName } = store.getState().drawer; + const { isOpen, drawerName } = store.getState().drawer; expect(type).toBe('drawer/closeDrawer'); - expect(open).toBe(false); + expect(isOpen).toBe(false); expect(drawerName).toEqual('none'); }); diff --git a/src/redux/drawer/drawer.reducers.ts b/src/redux/drawer/drawer.reducers.ts index 47b8ef6dd7ff78df197ba96bd753bb80d99b186b..ade3a5c12c7508f42f126f0e9d604031b6487d61 100644 --- a/src/redux/drawer/drawer.reducers.ts +++ b/src/redux/drawer/drawer.reducers.ts @@ -3,10 +3,10 @@ import { DrawerState } from '@/redux/drawer/drawer.types'; import { PathName } from '@/types/pathName'; export const openDrawerReducer = (state: DrawerState, action: PayloadAction<PathName>): void => { - state.open = true; + state.isOpen = true; state.drawerName = action.payload; }; export const closeDrawerReducer = (state: DrawerState): void => { - state.open = false; + state.isOpen = false; }; diff --git a/src/redux/drawer/drawer.selectors.ts b/src/redux/drawer/drawer.selectors.ts index a49b98fffb4585ab3afef535d62b364377f20a6b..0ae254bd997a14d0d3f09301f7eae2cffaff7ecc 100644 --- a/src/redux/drawer/drawer.selectors.ts +++ b/src/redux/drawer/drawer.selectors.ts @@ -1,4 +1,5 @@ import { createSelector } from '@reduxjs/toolkit'; import { rootSelector } from '@/redux/root/root.selectors'; -export const drawerDataSelector = createSelector(rootSelector, state => state.drawer); +export const drawerSelector = createSelector(rootSelector, state => state.drawer); +export const isDrawerOpenSelector = createSelector(drawerSelector, state => state.isOpen); diff --git a/src/redux/drawer/drawer.slice.ts b/src/redux/drawer/drawer.slice.ts index 4c71fda1792e323ce63c52292f06bf13cb1f044f..aa1f2e55a4265c92337f33f449e2a10be9b7df69 100644 --- a/src/redux/drawer/drawer.slice.ts +++ b/src/redux/drawer/drawer.slice.ts @@ -3,7 +3,7 @@ import { DrawerState } from '@/redux/drawer/drawer.types'; import { openDrawerReducer, closeDrawerReducer } from './drawer.reducers'; const initialState: DrawerState = { - open: false, + isOpen: false, drawerName: 'none', }; diff --git a/src/redux/drawer/drawer.types.ts b/src/redux/drawer/drawer.types.ts index a0ad70b8b7e1cf4f7555eaf15ffc265dd9b19206..8b4bf47a33fd92a276a2fc45e96863db6c33cdf8 100644 --- a/src/redux/drawer/drawer.types.ts +++ b/src/redux/drawer/drawer.types.ts @@ -1,4 +1,4 @@ export type DrawerState = { - open: boolean; + isOpen: boolean; drawerName: 'none' | 'search' | 'project-info' | 'plugins' | 'export' | 'legend'; }; diff --git a/src/redux/drugs/drugs.selectors.ts b/src/redux/drugs/drugs.selectors.ts index b67a8cb086dfed2ffdebe53f6d446865358ee459..cca30ff272f61d3ae75d24ed4a21786ffe37e28b 100644 --- a/src/redux/drugs/drugs.selectors.ts +++ b/src/redux/drugs/drugs.selectors.ts @@ -1,6 +1,11 @@ import { rootSelector } from '@/redux/root/root.selectors'; import { createSelector } from '@reduxjs/toolkit'; +const SIZE_OF_EMPTY_ARRAY = 0; + export const drugsSelector = createSelector(rootSelector, state => state.drugs); export const loadingDrugsStatusSelector = createSelector(drugsSelector, state => state.loading); +export const numberOfDrugsSelector = createSelector(drugsSelector, state => + state.data ? state.data.length : SIZE_OF_EMPTY_ARRAY, +); diff --git a/src/redux/search/search.slice.ts b/src/redux/search/search.slice.ts index 73930e3374bc72c333bc89ec8be066d1e961c241..360bfa689ed19e83f6474176a4d02fe7c4512384 100644 --- a/src/redux/search/search.slice.ts +++ b/src/redux/search/search.slice.ts @@ -4,10 +4,6 @@ import { createSlice } from '@reduxjs/toolkit'; const initialState: SearchState = { searchValue: '', - searchResult: { - content: '', - drugs: '', - }, loading: 'idle', }; diff --git a/src/redux/search/search.types.ts b/src/redux/search/search.types.ts index 1faf1b72e2f18b7a340e425c0af6a611712e95d8..45380f011abc19d556077abbbc62021f6c1fa016 100644 --- a/src/redux/search/search.types.ts +++ b/src/redux/search/search.types.ts @@ -1,12 +1,6 @@ import { Loading } from '@/types/loadingState'; -export interface SearchResult { - content: string; - drugs: string; -} - export interface SearchState { searchValue: string; - searchResult: SearchResult; loading: Loading; } diff --git a/src/redux/store.ts b/src/redux/store.ts index 1999d33f1373323b288bed176473fca135a77067..841a79b8b04c985c5b5463daf07f18bbbdd9f75f 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -22,6 +22,7 @@ export const store = configureStore({ devTools: true, }); +export type StoreType = typeof store; // Infer the `RootState` and `AppDispatch` types from the store itself export type RootState = ReturnType<typeof store.getState>; // Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState} diff --git a/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx b/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx index 32942ae9a996c6f83c3402b9f904a4d27e1351a3..09603e0609e575f13e43dd5d4facdd18eb6763cf 100644 --- a/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx +++ b/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx @@ -1,14 +1,23 @@ -import { Icon } from '@/shared/Icon'; import { AccordionItemButton as AIB } from 'react-accessible-accordion'; import './AccordionItemButton.style.css'; +import { Variant } from './AccordionItemButton.types'; +import { getIcon } from './AccordionItemButton.utils'; interface AccordionItemButtonProps { children: React.ReactNode; + variant?: Variant; } -export const AccordionItemButton = ({ children }: AccordionItemButtonProps): JSX.Element => ( - <AIB className="accordion-button flex flex-row flex-nowrap justify-between"> - {children} - <Icon name="chevron-down" className="arrow-button h-6 w-6 fill-font-500" /> - </AIB> -); +export const AccordionItemButton = ({ + children, + variant = 'expandable', +}: AccordionItemButtonProps): JSX.Element => { + const ButtonIcon = getIcon(variant); + + return ( + <AIB className="accordion-button flex flex-row flex-nowrap justify-between"> + {children} + {ButtonIcon} + </AIB> + ); +}; diff --git a/src/shared/Accordion/AccordionItemButton/AccordionItemButton.types.ts b/src/shared/Accordion/AccordionItemButton/AccordionItemButton.types.ts new file mode 100644 index 0000000000000000000000000000000000000000..ae2054d23759807ce59d282febcc2c5a091ef045 --- /dev/null +++ b/src/shared/Accordion/AccordionItemButton/AccordionItemButton.types.ts @@ -0,0 +1 @@ +export type Variant = 'expandable' | 'non-expandable'; diff --git a/src/shared/Accordion/AccordionItemButton/AccordionItemButton.utils.tsx b/src/shared/Accordion/AccordionItemButton/AccordionItemButton.utils.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2d597a92b01655e0b73d72516d30717caa3c75c1 --- /dev/null +++ b/src/shared/Accordion/AccordionItemButton/AccordionItemButton.utils.tsx @@ -0,0 +1,11 @@ +import { Icon } from '@/shared/Icon'; +import { Variant } from './AccordionItemButton.types'; + +export const getIcon = (variant: Variant): JSX.Element => { + const variantsIcons: Record<Variant, JSX.Element> = { + expandable: <Icon name="chevron-down" className="arrow-button h-6 w-6 fill-font-500" />, + 'non-expandable': <Icon name="chevron-right" className="h-6 w-6 fill-font-500" />, + }; + + return variantsIcons[variant]; +}; diff --git a/src/utils/testing/getReduxWrapperWithStore.tsx b/src/utils/testing/getReduxWrapperWithStore.tsx new file mode 100644 index 0000000000000000000000000000000000000000..37e3cd5a97ded56e8b8c535231261ff5a2768dcc --- /dev/null +++ b/src/utils/testing/getReduxWrapperWithStore.tsx @@ -0,0 +1,59 @@ +import { store } from '@/redux/store'; +import { configureStore } from '@reduxjs/toolkit'; +import { Provider } from 'react-redux'; +import bioEntityContentsReducer from '@/redux/bioEntityContents/bioEntityContents.slice'; +import chemicalsReducer from '@/redux/chemicals/chemicals.slice'; +import drawerReducer from '@/redux/drawer/drawer.slice'; +import drugsReducer from '@/redux/drugs/drugs.slice'; +import mirnasReducer from '@/redux/mirnas/mirnas.slice'; +import projectReducer from '@/redux/project/project.slice'; +import searchReducer from '@/redux/search/search.slice'; +import { SearchState } from '@/redux/search/search.types'; +import { ProjectState } from '@/redux/project/project.types'; +import { DrugsState } from '@/redux/drugs/drugs.types'; +import { MirnasState } from '@/redux/mirnas/mirnas.types'; +import { ChemicalsState } from '@/redux/chemicals/chemicals.types'; +import { BioEntityContentsState } from '@/redux/bioEntityContents/bioEntityContents.types'; +import { DrawerState } from '@/redux/drawer/drawer.types'; + +interface WrapperProps { + children: React.ReactNode; +} + +export type InitialStoreState = { + search?: SearchState; + project?: ProjectState; + drugs?: DrugsState; + mirnas?: MirnasState; + chemicals?: ChemicalsState; + bioEntityContents?: BioEntityContentsState; + drawer?: DrawerState; +}; + +type GetReduxWrapperUsingSliceReducer = (initialState?: InitialStoreState) => { + Wrapper: ({ children }: WrapperProps) => JSX.Element; + store: typeof store; +}; + +export const getReduxWrapperWithStore: GetReduxWrapperUsingSliceReducer = ( + preloadedState: InitialStoreState = {}, +) => { + const testStore = configureStore({ + reducer: { + search: searchReducer, + project: projectReducer, + drugs: drugsReducer, + mirnas: mirnasReducer, + chemicals: chemicalsReducer, + bioEntityContents: bioEntityContentsReducer, + drawer: drawerReducer, + }, + preloadedState, + }); + + const Wrapper = ({ children }: WrapperProps): JSX.Element => ( + <Provider store={testStore}>{children}</Provider> + ); + + return { Wrapper, store: testStore }; +};