Skip to content
Snippets Groups Projects
Commit e095cdf5 authored by Mateusz Bolewski's avatar Mateusz Bolewski
Browse files

Merge branch 'feat/multisearch-tabs' into 'development'

feat(search): multisearch tabs

See merge request !67
parents fe467ff7 e0334ea2
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...,!67feat(search): multisearch tabs
Pipeline #82426 passed
Showing
with 6672 additions and 6538 deletions
......@@ -66,4 +66,22 @@ describe('SearchBar - component', () => {
});
renderComponent();
});
it('should change selected search element when user search another', () => {
const { store } = renderComponent();
const input = screen.getByTestId<HTMLInputElement>('search-input');
fireEvent.change(input, { target: { value: 'nadh' } });
const button = screen.getByRole('button');
fireEvent.click(button);
const {
drawer: {
searchDrawerState: { selectedSearchElement },
},
} = store.getState();
expect(selectedSearchElement).toBe('nadh');
});
});
import lensIcon from '@/assets/vectors/icons/lens.svg';
import { isDrawerOpenSelector } from '@/redux/drawer/drawer.selectors';
import { openSearchDrawerWithSelectedTab } from '@/redux/drawer/drawer.slice';
import { openSearchDrawerWithSelectedTab, selectTab } from '@/redux/drawer/drawer.slice';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import {
isPendingSearchStatusSelector,
......@@ -24,6 +24,8 @@ export const SearchBar = (): JSX.Element => {
const openSearchDrawerIfClosed = (defaultSearchTab: string): void => {
if (!isDrawerOpen) {
dispatch(openSearchDrawerWithSelectedTab(defaultSearchTab));
} else {
dispatch(selectTab(defaultSearchTab));
}
};
......
......@@ -9,7 +9,7 @@ import { StoreType } from '@/redux/store';
import { BioEntity } from '@/types/models';
import { BioEntitiesPinsListItem } from './BioEntitiesPinsListItem.component';
const BIO_ENTITY = bioEntitiesContentFixture[2].bioEntity;
const BIO_ENTITY = bioEntitiesContentFixture[0].bioEntity;
const renderComponent = (
name: string,
......@@ -30,11 +30,11 @@ const renderComponent = (
);
};
describe('BioEntitiesPinsList - component ', () => {
describe('BioEntitiesPinsListItem - component ', () => {
it('should display name of bio entity element', () => {
renderComponent(BIO_ENTITY.name, BIO_ENTITY);
const bioEntityName = bioEntitiesContentFixture[2].bioEntity.fullName || '';
const bioEntityName = BIO_ENTITY.fullName || '';
expect(screen.getByText(bioEntityName, { exact: false })).toBeInTheDocument();
});
......@@ -46,25 +46,27 @@ describe('BioEntitiesPinsList - component ', () => {
expect(screen.getByText(bioEntitySymbol, { exact: false })).toBeInTheDocument();
});
it('should display empty string when symbol does not exist', () => {
renderComponent(
bioEntitiesContentFixture[1].bioEntity.name,
bioEntitiesContentFixture[1].bioEntity,
);
const bioEntity = {
...bioEntitiesContentFixture[0].bioEntity,
symbol: null,
};
renderComponent(bioEntity.name, bioEntity);
expect(screen.getAllByTestId('bio-entity-symbol')[0].textContent).toHaveLength(0);
});
it('should display string type of bio entity element', () => {
renderComponent(BIO_ENTITY.name, BIO_ENTITY);
const bioEntityStringType = bioEntitiesContentFixture[2].bioEntity.stringType;
const bioEntityStringType = BIO_ENTITY.stringType;
expect(screen.getByText(bioEntityStringType, { exact: false })).toBeInTheDocument();
});
it('should display synonyms of bio entity element', () => {
renderComponent(BIO_ENTITY.name, BIO_ENTITY);
const firstBioEntitySynonym = bioEntitiesContentFixture[2].bioEntity.synonyms[0];
const secondBioEntitySynonym = bioEntitiesContentFixture[2].bioEntity.synonyms[1];
const firstBioEntitySynonym = BIO_ENTITY.synonyms[0];
const secondBioEntitySynonym = BIO_ENTITY.synonyms[1];
expect(screen.getByText(firstBioEntitySynonym, { exact: false })).toBeInTheDocument();
expect(screen.getByText(secondBioEntitySynonym, { exact: false })).toBeInTheDocument();
......@@ -72,11 +74,10 @@ describe('BioEntitiesPinsList - component ', () => {
it('should display list of references for pin', () => {
renderComponent(BIO_ENTITY.name, BIO_ENTITY);
const firstPinReferenceType = bioEntitiesContentFixture[2].bioEntity.references[0].type;
const firstPinReferenceResource = bioEntitiesContentFixture[2].bioEntity.references[0].resource;
const secondPinReferenceType = bioEntitiesContentFixture[2].bioEntity.references[1].type;
const secondPinReferenceResource =
bioEntitiesContentFixture[2].bioEntity.references[1].resource;
const firstPinReferenceType = BIO_ENTITY.references[0].type;
const firstPinReferenceResource = BIO_ENTITY.references[0].resource;
const secondPinReferenceType = BIO_ENTITY.references[1].type;
const secondPinReferenceResource = BIO_ENTITY.references[1].resource;
expect(screen.getByText(firstPinReferenceType, { exact: false })).toBeInTheDocument();
expect(screen.getByText(firstPinReferenceResource, { exact: false })).toBeInTheDocument();
......
......@@ -73,7 +73,7 @@ describe('BioEntitiesAccordion - component', () => {
});
expect(screen.getByText('Content (10)')).toBeInTheDocument();
expect(screen.getByText('Core PD map (8)')).toBeInTheDocument();
expect(screen.getByText('Core PD map (5)')).toBeInTheDocument();
expect(screen.getByText('Histamine signaling (2)')).toBeInTheDocument();
});
});
......@@ -3,22 +3,12 @@ import { DrugsAccordion } from '@/components/Map/Drawer/SearchDrawerWrapper/Grou
import { ChemicalsAccordion } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/ChemicalsAccordion';
import { MirnaAccordion } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/MirnaAccordion';
import { Accordion } from '@/shared/Accordion';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { searchValueSelector } from '@/redux/search/search.selectors';
export const CLOSE_BUTTON_ROLE = 'close-drawer-button';
export const GroupedSearchResults = (): JSX.Element => {
const searchValue = useAppSelector(searchValueSelector);
return (
<div className="flex flex-col" data-testid="grouped-search-results">
<div className="flex items-center justify-between border-b border-b-divide px-6">
<div className=" py-8 text-xl">
<span className="font-normal">Search: </span>
<span className="font-semibold">{searchValue}</span>
</div>
</div>
<div className="px-6">
<Accordion allowZeroExpanded>
<BioEntitiesAccordion />
......
......@@ -76,11 +76,6 @@ describe('PinsList - component ', () => {
it('should not display list of bio enities when bioEntity is searched', () => {
renderComponent([], 'bioEntity');
expect(screen.queryByTestId('pins-list')).toBeNull();
});
it('should not display list of pins when none is searched', () => {
renderComponent([], 'none');
expect(screen.queryByTestId('pins-list')).toBeNull();
});
});
......@@ -24,6 +24,7 @@ const INITIAL_STATE: InitialStoreState = {
listOfBioEnitites: [],
selectedSearchElement: 'aspirin',
},
reactionDrawerState: {},
},
drugs: {
data: [
......
import { act, render, screen } from '@testing-library/react';
import { StoreType } from '@/redux/store';
import {
InitialStoreState,
getReduxWrapperWithStore,
} from '@/utils/testing/getReduxWrapperWithStore';
import { SearchDrawerTabs } from './SearchDrawerTabs.component';
const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState);
return (
render(
<Wrapper>
<SearchDrawerTabs />
</Wrapper>,
),
{
store,
}
);
};
describe('SearchDrawerTabs - component', () => {
it('should display tabs with search values', () => {
renderComponent({
search: {
searchValue: ['aspirin', 'nadh'],
loading: 'idle',
perfectMatch: false,
},
});
expect(screen.getByText('aspirin')).toBeInTheDocument();
expect(screen.getByText('nadh')).toBeInTheDocument();
});
it('second test', async () => {
const { store } = renderComponent({
search: {
searchValue: ['aspirin', 'nadh'],
loading: 'idle',
perfectMatch: false,
},
});
const tabButton = screen.getByText('nadh');
await act(() => {
tabButton.click();
});
const {
drawer: {
searchDrawerState: { selectedSearchElement },
},
} = store.getState();
expect(selectedSearchElement).toBe('nadh');
});
});
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { searchValueSelector } from '@/redux/search/search.selectors';
import { currentSelectedSearchElement } from '@/redux/drawer/drawer.selectors';
import { selectTab } from '@/redux/drawer/drawer.slice';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { twMerge } from 'tailwind-merge';
export const SearchDrawerTabs = (): JSX.Element => {
const dispatch = useAppDispatch();
const searchValue = useAppSelector(searchValueSelector);
const selectedSearchValue = useAppSelector(currentSelectedSearchElement);
const isActive = (value: string): boolean => selectedSearchValue === value;
const onTabClick = (value: string): void => {
dispatch(selectTab(value));
};
return (
<div className="flex items-center justify-between border-b border-b-divide px-6">
<div className="text-center text-sm">
<ul className="-mb-px flex flex-wrap">
{searchValue.map(value => (
<li className="me-2" key={value}>
<button
type="button"
className={twMerge(
'inline-block rounded-t-lg border-x border-t border-[#070130] border-b-transparent px-2 py-1 font-semibold leading-6',
isActive(value) ? 'bg-[#F7F7F8]' : 'border-[#EEE] font-normal text-[#6A6977]',
)}
onClick={(): void => onTabClick(value)}
>
{value}
</button>
</li>
))}
</ul>
</div>
</div>
);
};
export { SearchDrawerTabs } from './SearchDrawerTabs.component';
......@@ -9,6 +9,7 @@ import { ResultsList } from '@/components/Map/Drawer/SearchDrawerWrapper/Results
import { GroupedSearchResults } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults';
import { BioEntitiesResultsList } from './BioEntitiesResultsList';
import { SearchDrawerHeader } from './SearchDrawerHeader';
import { SearchDrawerTabs } from './SearchDrawerTabs';
export const SearchDrawerWrapper = (): JSX.Element => {
const currentStep = useSelector(currentStepDrawerStateSelector);
......@@ -20,6 +21,7 @@ export const SearchDrawerWrapper = (): JSX.Element => {
return (
<>
<SearchDrawerHeader />
<SearchDrawerTabs />
<div data-testid="search-drawer-content">
{/* first step for displaying search results, drawers etc */}
{currentStep === STEP.FIRST && <GroupedSearchResults />}
......
......@@ -37,7 +37,7 @@ export const bioEntitySchema = z.object({
formula: z.string().nullable(),
glyph: glyphSchema.nullable(),
activity: z.boolean(),
structuralState: structuralStateSchema.nullable(),
structuralState: z.optional(structuralStateSchema.nullable()),
hypothetical: z.boolean().nullable(),
boundaryCondition: z.boolean(),
constant: z.boolean(),
......
......@@ -6,10 +6,10 @@ export const modificationResiduesSchema = z.object({
id: z.number(),
idModificationResidue: z.string(),
name: z.string(),
position: positionSchema,
position: z.optional(positionSchema),
z: z.number(),
borderColor: colorSchema,
state: z.union([z.string(), z.number()]).nullable(),
state: z.optional(z.union([z.string(), z.number()]).nullable()),
size: z.number(),
elementId: z.string(),
});
......@@ -12,6 +12,7 @@ import drawerReducer, {
openDrawer,
openSearchDrawerWithSelectedTab,
openSubmapsDrawer,
selectTab,
} from './drawer.slice';
import type { DrawerState } from './drawer.types';
......@@ -81,6 +82,16 @@ describe('drawer reducer', () => {
expect(drawerName).toEqual('submaps');
});
it('should update the store after selectTab action', async () => {
const { type } = await store.dispatch(selectTab('nadh'));
const {
searchDrawerState: { selectedSearchElement },
} = store.getState().drawer;
expect(type).toBe('drawer/selectTab');
expect(selectedSearchElement).toBe('nadh');
});
it('should update the store after closeDrawerReducer action', async () => {
const { type } = await store.dispatch(closeDrawer());
const {
......
......@@ -27,6 +27,14 @@ export const openSubmapsDrawerReducer = (state: DrawerState): void => {
state.drawerName = 'submaps';
};
export const selectTabReducer = (
state: DrawerState,
action: OpenSearchDrawerWithSelectedTabReducerAction,
): void => {
state.searchDrawerState.currentStep = STEP.FIRST;
state.searchDrawerState.selectedSearchElement = action.payload;
};
export const closeDrawerReducer = (state: DrawerState): void => {
state.isOpen = false;
state.drawerName = 'none';
......
......@@ -11,6 +11,7 @@ import {
openReactionDrawerByIdReducer,
openSearchDrawerWithSelectedTabReducer,
openSubmapsDrawerReducer,
selectTabReducer,
} from './drawer.reducers';
import { DRAWER_INITIAL_STATE } from './drawer.constants';
......@@ -21,6 +22,7 @@ const drawerSlice = createSlice({
openDrawer: openDrawerReducer,
openSearchDrawerWithSelectedTab: openSearchDrawerWithSelectedTabReducer,
openSubmapsDrawer: openSubmapsDrawerReducer,
selectTab: selectTabReducer,
closeDrawer: closeDrawerReducer,
displayDrugsList: displayDrugsListReducer,
displayChemicalsList: displayChemicalsListReducer,
......@@ -36,6 +38,7 @@ export const {
openDrawer,
openSearchDrawerWithSelectedTab,
openSubmapsDrawer,
selectTab,
closeDrawer,
displayDrugsList,
displayChemicalsList,
......
......@@ -62,6 +62,7 @@ export const drawerSearchChemicalsStepTwoFixture: DrawerState = {
listOfBioEnitites: [],
selectedSearchElement: '',
},
reactionDrawerState: {},
};
export const drawerSearchMirnaStepTwoFixture: DrawerState = {
......@@ -74,4 +75,5 @@ export const drawerSearchMirnaStepTwoFixture: DrawerState = {
listOfBioEnitites: [],
selectedSearchElement: '',
},
reactionDrawerState: {},
};
This diff is collapsed.
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