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

feat: merge changes

parents 7088d94a ba85bc5c
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...,!110feat: add display reaction overlay (MIN-215)
Pipeline #84881 passed
Showing
with 995 additions and 790 deletions
source diff could not be displayed: it is too large. Options to address this: view the blob.
......@@ -21036,8 +21036,7 @@
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lodash.camelcase": {
"version": "4.3.0",
......
......@@ -14,8 +14,8 @@
"check-types": "tsc --pretty --noEmit",
"prepare": "husky install",
"postinstall": "husky install",
"test": "jest --config ./jest.config.ts --transformIgnorePatterns 'node_modules/(?!@toolz/allow-react)/'",
"test:watch": "jest --watch --config ./jest.config.ts --transformIgnorePatterns 'node_modules/(?!(ol|geotiff|quick-lru|.*\\.mjs$))'",
"test": "jest --config ./jest.config.ts --transformIgnorePatterns 'node_modules/(?!(ol|geotiff|quick-lru|color-space|color-rgba|color-parse|.*\\.mjs$))'",
"test:watch": "jest --watch --config ./jest.config.ts --transformIgnorePatterns 'node_modules/(?!(ol|geotiff|quick-lru|color-space|color-rgba|color-parse|.*\\.mjs$))'",
"test:ci": "jest --config ./jest.config.ts --collectCoverage --coverageDirectory=\"./coverage\" --ci --reporters=default --reporters=jest-junit --watchAll=false --passWithNoTests --transformIgnorePatterns 'node_modules/(?!(ol|geotiff|quick-lru|color-space|color-rgba|color-parse|.*\\.mjs$))'",
"test:coverage": "jest --watchAll --coverage --config ./jest.config.ts --transformIgnorePatterns 'node_modules/(?!(ol|geotiff|quick-lru|.*\\.mjs$))'",
"test:coveragee": "jest --coverage --transformIgnorePatterns 'node_modules/(?!(ol|geotiff|quick-lru|.*\\.mjs$))'",
......
......@@ -65,7 +65,12 @@ describe('SearchBar - component', () => {
query: { searchValue: 'aspirin;nadh' },
});
renderComponent();
const input = screen.getByTestId<HTMLInputElement>('search-input');
expect(input.value).toBe('aspirin;nadh');
});
it('should change selected search element when user search another', () => {
const { store } = renderComponent();
const input = screen.getByTestId<HTMLInputElement>('search-input');
......
......@@ -8,7 +8,8 @@ import {
} from '@/redux/search/search.selectors';
import { getSearchData } from '@/redux/search/search.thunks';
import Image from 'next/image';
import { ChangeEvent, KeyboardEvent, useState } from 'react';
import { ChangeEvent, KeyboardEvent, useCallback, useEffect, useState } from 'react';
import { useRouter } from 'next/router';
import { useSelector } from 'react-redux';
import { getDefaultSearchTab, getSearchValuesArrayAndTrimToSeven } from './SearchBar.utils';
......@@ -20,6 +21,14 @@ export const SearchBar = (): JSX.Element => {
const isDrawerOpen = useSelector(isDrawerOpenSelector);
const isPerfectMatch = useSelector(perfectMatchSelector);
const dispatch = useAppDispatch();
const router = useRouter();
const updateSearchValueFromQueryParam = useCallback((): void => {
const { searchValue: searchValueQueryParam } = router.query;
if (typeof searchValueQueryParam === 'string') {
setSearchValue(searchValueQueryParam);
}
}, [router.query]);
const openSearchDrawerIfClosed = (defaultSearchTab: string): void => {
if (!isDrawerOpen) {
......@@ -49,6 +58,10 @@ export const SearchBar = (): JSX.Element => {
}
};
useEffect(() => {
updateSearchValueFromQueryParam();
}, [updateSearchValueFromQueryParam]);
return (
<div className="relative" data-testid="search-bar">
<input
......
import { render, screen, waitFor } from '@testing-library/react';
import {
InitialStoreState,
getReduxWrapperWithStore,
} from '@/utils/testing/getReduxWrapperWithStore';
import { StoreType } from '@/redux/store';
import { statisticsFixture } from '@/models/fixtures/statisticsFixture';
import { act } from 'react-dom/test-utils';
import { Annotations } from './Annotations.component';
const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState);
return (
render(
<Wrapper>
<Annotations />
</Wrapper>,
),
{
store,
}
);
};
describe('Annotations - component', () => {
it('should display annotations checkboxes when fetching data is successful', async () => {
renderComponent({
statistics: {
data: {
...statisticsFixture,
elementAnnotations: {
compartment: 1,
pathway: 0,
},
},
loading: 'succeeded',
error: {
message: '',
name: '',
},
},
});
const navigationButton = screen.getByTestId('accordion-item-button');
act(() => {
navigationButton.click();
});
expect(screen.getByText('Select annotations')).toBeInTheDocument();
await waitFor(() => {
expect(screen.getByTestId('checkbox-filter')).toBeInTheDocument();
expect(screen.getByLabelText('compartment')).toBeInTheDocument();
expect(screen.getByLabelText('search-input')).toBeInTheDocument();
});
});
it('should not display annotations checkboxes when fetching data fails', async () => {
renderComponent({
statistics: {
data: undefined,
loading: 'failed',
error: {
message: '',
name: '',
},
},
});
expect(screen.getByText('Select annotations')).toBeInTheDocument();
const navigationButton = screen.getByTestId('accordion-item-button');
act(() => {
navigationButton.click();
});
expect(screen.queryByTestId('checkbox-filter')).not.toBeInTheDocument();
});
it('should not display annotations checkboxes when fetched data is empty object', async () => {
renderComponent({
statistics: {
data: {
...statisticsFixture,
elementAnnotations: {},
},
loading: 'failed',
error: {
message: '',
name: '',
},
},
});
expect(screen.getByText('Select annotations')).toBeInTheDocument();
const navigationButton = screen.getByTestId('accordion-item-button');
act(() => {
navigationButton.click();
});
expect(screen.queryByTestId('checkbox-filter')).not.toBeInTheDocument();
});
it('should display loading message when fetching data is pending', async () => {
renderComponent({
statistics: {
data: undefined,
loading: 'pending',
error: {
message: '',
name: '',
},
},
});
expect(screen.getByText('Select annotations')).toBeInTheDocument();
const navigationButton = screen.getByTestId('accordion-item-button');
act(() => {
navigationButton.click();
});
expect(screen.getByText('Loading...')).toBeInTheDocument();
});
});
/* eslint-disable no-magic-numbers */
import {
Accordion,
AccordionItem,
AccordionItemButton,
AccordionItemHeading,
AccordionItemPanel,
} from '@/shared/Accordion';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import {
elementAnnotationsSelector,
loadingStatisticsSelector,
} from '@/redux/statistics/statistics.selectors';
import { CheckboxFilter } from '../CheckboxFilter';
export const Annotations = (): React.ReactNode => {
const loadingStatistics = useAppSelector(loadingStatisticsSelector);
const elementAnnotations = useAppSelector(elementAnnotationsSelector);
const isPending = loadingStatistics === 'pending';
const mappedElementAnnotations = elementAnnotations
? Object.keys(elementAnnotations)?.map(el => ({ id: el, label: el }))
: [];
return (
<Accordion allowZeroExpanded>
<AccordionItem>
<AccordionItemHeading>
<AccordionItemButton>Select annotations</AccordionItemButton>
</AccordionItemHeading>
<AccordionItemPanel>
{isPending && <p>Loading...</p>}
{!isPending && mappedElementAnnotations && mappedElementAnnotations.length > 0 && (
<CheckboxFilter options={mappedElementAnnotations} />
)}
</AccordionItemPanel>
</AccordionItem>
</Accordion>
);
};
......@@ -4,7 +4,7 @@ import React, { useEffect, useState } from 'react';
import lensIcon from '@/assets/vectors/icons/lens.svg';
import { twMerge } from 'tailwind-merge';
type CheckboxItem = { id: string; label: string };
export type CheckboxItem = { id: string; label: string };
type CheckboxFilterProps = {
options: CheckboxItem[];
......
export { Annotations } from './Annotations.component';
import { useEffect } from 'react';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { modelsDataSelector } from '@/redux/models/models.selectors';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { getCompartmentPathways } from '@/redux/compartmentPathways/compartmentPathways.thunks';
import { Annotations } from '../Annotations';
import { Types } from './Types';
import { IncludedCompartmentPathways } from './IncludedCompartmentPathways ';
import { ExcludedCompartmentPathways } from './ExcludedCompartmentPathways';
import { Columns } from './Columns';
import { getModelsIds } from './Elements.utils';
import { Export } from '../ExportCompound';
export const Elements = (): React.ReactNode => {
const models = useAppSelector(modelsDataSelector);
const dispatch = useAppDispatch();
useEffect(() => {
const modelsIds = getModelsIds(models);
dispatch(getCompartmentPathways(modelsIds));
}, [dispatch, models]);
return (
<div data-testid="elements-tab">
<Types />
<Columns />
<Annotations />
<IncludedCompartmentPathways />
<ExcludedCompartmentPathways />
<Export>
<Export.Types />
<Export.Columns />
<Export.Annotations />
<Export.IncludedCompartmentPathways />
<Export.ExcludedCompartmentPathways />
<Export.DownloadElements />
</Export>
</div>
);
};
/* eslint-disable no-magic-numbers */
import { CompartmentPathwayDetails, MapModel } from '@/types/models';
type AddedNames = { [key: string]: number };
type CheckboxElement = { id: string; label: string };
type CheckboxElements = CheckboxElement[];
export const getCompartmentPathwaysCheckboxElements = (
items: CompartmentPathwayDetails[],
): CheckboxElements => {
const addedNames: AddedNames = {};
const setNameToIdIfUndefined = (item: CompartmentPathwayDetails): void => {
if (addedNames[item.name] === undefined) {
addedNames[item.name] = item.id;
}
};
items.forEach(setNameToIdIfUndefined);
const parseIdAndLabel = ([name, id]: [name: string, id: number]): CheckboxElement => ({
id: id.toString(),
label: name,
});
const sortByLabel = (a: CheckboxElement, b: CheckboxElement): number => {
if (a.label > b.label) return 1;
return -1;
};
const elements = Object.entries(addedNames).map(parseIdAndLabel).sort(sortByLabel);
return elements;
};
export const getModelsIds = (models: MapModel[] | undefined): number[] => {
if (!models) return [];
return models.map(model => model.idObject);
};
/* eslint-disable no-magic-numbers */
import { useContext } from 'react';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import {
elementAnnotationsSelector,
loadingStatisticsSelector,
} from '@/redux/statistics/statistics.selectors';
import { ZERO } from '@/constants/common';
import { CheckboxFilter } from '../../CheckboxFilter';
import { CollapsibleSection } from '../../CollapsibleSection';
import { ExportContext } from '../ExportCompound.context';
export const Annotations = (): React.ReactNode => {
const { setAnnotations } = useContext(ExportContext);
const loadingStatistics = useAppSelector(loadingStatisticsSelector);
const elementAnnotations = useAppSelector(elementAnnotationsSelector);
const isPending = loadingStatistics === 'pending';
......@@ -19,8 +22,8 @@ export const Annotations = (): React.ReactNode => {
return (
<CollapsibleSection title="Select annotations">
{isPending && <p>Loading...</p>}
{!isPending && mappedElementAnnotations && mappedElementAnnotations.length > 0 && (
<CheckboxFilter options={mappedElementAnnotations} />
{!isPending && mappedElementAnnotations && mappedElementAnnotations.length > ZERO && (
<CheckboxFilter options={mappedElementAnnotations} onCheckedChange={setAnnotations} />
)}
</CollapsibleSection>
);
......
import { useContext } from 'react';
import { CheckboxFilter } from '../../CheckboxFilter';
import { CollapsibleSection } from '../../CollapsibleSection';
import { COLUMNS } from './Columns.constants';
import { ExportContext } from '../ExportCompound.context';
export const Columns = (): React.ReactNode => (
<CollapsibleSection title="Select column">
<CheckboxFilter options={COLUMNS} isSearchEnabled={false} />
</CollapsibleSection>
);
export const Columns = (): React.ReactNode => {
const { setColumns } = useContext(ExportContext);
return (
<CollapsibleSection title="Select column">
<CheckboxFilter options={COLUMNS} isSearchEnabled={false} onCheckedChange={setColumns} />
</CollapsibleSection>
);
};
import { useContext } from 'react';
import { Button } from '@/shared/Button';
import { ExportContext } from '../ExportCompound.context';
export const DownloadElements = (): React.ReactNode => {
const { handleDownloadElements } = useContext(ExportContext);
return (
<div className="mt-6">
<Button onClick={handleDownloadElements}>Download</Button>
</div>
);
};
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