Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • minerva/frontend
1 result
Show changes
Showing
with 517 additions and 48 deletions
import { Button } from '@/shared/Button';
import { Input } from '@/shared/Input';
import { useLoadPluginFromUrl } from './hooks/useLoadPluginFromUrl';
export const LoadPluginFromUrl = (): JSX.Element => {
const { handleChangePluginUrl, handleLoadPlugin, isPending, pluginUrl } = useLoadPluginFromUrl();
return (
<div className="flex w-full">
<label className="flex w-full flex-col gap-2 text-sm text-cetacean-blue">
<span>URL:</span>
<Input
className="h-10 w-full flex-none bg-cultured p-3"
type="url"
value={pluginUrl}
onChange={handleChangePluginUrl}
data-testid="load-plugin-input-url"
/>
</label>
<Button
variantStyles="secondary"
className="h-10 self-end rounded-e rounded-s text-xs font-medium"
onClick={handleLoadPlugin}
disabled={isPending}
data-testid="load-plugin-button"
>
Load
</Button>
</div>
);
};
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { activePluginsDataSelector } from '@/redux/plugins/plugins.selectors';
import { PluginsManager } from '@/services/pluginsManager';
import axios from 'axios';
import { ChangeEvent, useMemo, useState } from 'react';
type UseLoadPluginReturnType = {
handleChangePluginUrl: (event: ChangeEvent<HTMLInputElement>) => void;
handleLoadPlugin: () => Promise<void>;
isPending: boolean;
pluginUrl: string;
};
export const useLoadPluginFromUrl = (): UseLoadPluginReturnType => {
const [pluginUrl, setPluginUrl] = useState('');
const [isLoading, setIsLoading] = useState(false);
const activePlugins = useAppSelector(activePluginsDataSelector);
const isPending = useMemo(
() => !pluginUrl || isLoading || !URL.canParse(pluginUrl),
[pluginUrl, isLoading],
);
const handleLoadPlugin = async (): Promise<void> => {
try {
setIsLoading(true);
const response = await axios(pluginUrl);
const pluginScript = response.data;
/* eslint-disable no-new-func */
const loadPlugin = new Function(pluginScript);
const hash = PluginsManager.setHashedPlugin({
pluginUrl,
pluginScript,
});
if (!(hash in activePlugins)) {
loadPlugin();
}
setPluginUrl('');
} finally {
setIsLoading(false);
}
};
const handleChangePluginUrl = (event: ChangeEvent<HTMLInputElement>): void => {
setPluginUrl(event.target.value);
};
return {
handleChangePluginUrl,
handleLoadPlugin,
isPending,
pluginUrl,
};
};
export { LoadPluginFromUrl } from './LoadPluginFromUrl.component';
import { privateActivePluginsSelector } from '@/redux/plugins/plugins.selectors';
import { useSelector } from 'react-redux';
import { LoadPlugin } from '../LoadPlugin';
export const PrivateActivePlugins = (): React.ReactNode => {
const privateActivePlugins = useSelector(privateActivePluginsSelector);
return privateActivePlugins.map(plugin => <LoadPlugin key={plugin.hash} plugin={plugin} />);
};
export { PrivateActivePlugins } from './PrivateActivePlugins.component';
import { publicPluginsListSelector } from '@/redux/plugins/plugins.selectors';
import React from 'react';
import { useSelector } from 'react-redux';
import { LoadPlugin } from '../LoadPlugin';
export const PublicPlugins = (): React.ReactNode => {
const publicPlugins = useSelector(publicPluginsListSelector);
return publicPlugins.map(plugin => <LoadPlugin key={plugin.hash} plugin={plugin} />);
};
export { PublicPlugins } from './PublicPlugins.component';
export { AvailablePluginsDrawer } from './AvailablePluginsDrawer.component';
......@@ -2,13 +2,14 @@ import { DRAWER_ROLE } from '@/components/Map/Drawer/Drawer.constants';
import { drawerSelector } from '@/redux/drawer/drawer.selectors';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { twMerge } from 'tailwind-merge';
import { ReactionDrawer } from './ReactionDrawer';
import { SearchDrawerWrapper as SearchDrawerContent } from './SearchDrawerWrapper';
import { SubmapsDrawer } from './SubmapsDrawer';
import { OverlaysDrawer } from './OverlaysDrawer';
import { AvailablePluginsDrawer } from './AvailablePluginsDrawer';
import { BioEntityDrawer } from './BioEntityDrawer/BioEntityDrawer.component';
import { ExportDrawer } from './ExportDrawer';
import { OverlaysDrawer } from './OverlaysDrawer';
import { ProjectInfoDrawer } from './ProjectInfoDrawer';
import { ReactionDrawer } from './ReactionDrawer';
import { SearchDrawerWrapper as SearchDrawerContent } from './SearchDrawerWrapper';
import { SubmapsDrawer } from './SubmapsDrawer';
export const Drawer = (): JSX.Element => {
const { isOpen, drawerName } = useAppSelector(drawerSelector);
......@@ -28,6 +29,7 @@ export const Drawer = (): JSX.Element => {
{isOpen && drawerName === 'bio-entity' && <BioEntityDrawer />}
{isOpen && drawerName === 'project-info' && <ProjectInfoDrawer />}
{isOpen && drawerName === 'export' && <ExportDrawer />}
{isOpen && drawerName === 'available-plugins' && <AvailablePluginsDrawer />}
</div>
);
};
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { fireEvent, render, screen } from '@testing-library/react';
import { CheckboxFilter } from './CheckboxFilter.component';
......@@ -9,14 +8,16 @@ const options = [
{ id: '3', label: 'Option 3' },
];
const currentOptions = [{ id: '2', label: 'Option 2' }];
describe('CheckboxFilter - component', () => {
it('should render CheckboxFilter properly', () => {
render(<CheckboxFilter options={options} />);
render(<CheckboxFilter options={options} currentOptions={[]} />);
expect(screen.getByTestId('search')).toBeInTheDocument();
});
it('should filter options based on search term', async () => {
render(<CheckboxFilter options={options} />);
render(<CheckboxFilter options={options} currentOptions={[]} />);
const searchInput = screen.getByLabelText('search-input');
fireEvent.change(searchInput, { target: { value: `Option 1` } });
......@@ -28,7 +29,26 @@ describe('CheckboxFilter - component', () => {
it('should handle checkbox value change', async () => {
const onCheckedChange = jest.fn();
render(<CheckboxFilter options={options} onCheckedChange={onCheckedChange} />);
render(
<CheckboxFilter currentOptions={[]} options={options} onCheckedChange={onCheckedChange} />,
);
const checkbox = screen.getByLabelText('Option 1');
fireEvent.click(checkbox);
expect(onCheckedChange).toHaveBeenCalledWith([{ id: '1', label: 'Option 1' }]);
});
it('should handle radio value change', async () => {
const onCheckedChange = jest.fn();
render(
<CheckboxFilter
currentOptions={[]}
type="radio"
options={options}
onCheckedChange={onCheckedChange}
/>,
);
const checkbox = screen.getByLabelText('Option 1');
fireEvent.click(checkbox);
......@@ -38,7 +58,9 @@ describe('CheckboxFilter - component', () => {
it('should call onFilterChange when searching new term', async () => {
const onFilterChange = jest.fn();
render(<CheckboxFilter options={options} onFilterChange={onFilterChange} />);
render(
<CheckboxFilter currentOptions={[]} options={options} onFilterChange={onFilterChange} />,
);
const searchInput = screen.getByLabelText('search-input');
fireEvent.change(searchInput, { target: { value: 'Option 1' } });
......@@ -46,7 +68,7 @@ describe('CheckboxFilter - component', () => {
expect(onFilterChange).toHaveBeenCalledWith([{ id: '1', label: 'Option 1' }]);
});
it('should display message when no elements are found', async () => {
render(<CheckboxFilter options={options} />);
render(<CheckboxFilter currentOptions={[]} options={options} />);
const searchInput = screen.getByLabelText('search-input');
fireEvent.change(searchInput, { target: { value: 'Nonexistent Option' } });
......@@ -55,13 +77,15 @@ describe('CheckboxFilter - component', () => {
});
it('should display message when options are empty', () => {
const onFilterChange = jest.fn();
render(<CheckboxFilter options={[]} onFilterChange={onFilterChange} />);
render(<CheckboxFilter currentOptions={[]} options={[]} onFilterChange={onFilterChange} />);
expect(screen.getByText('No matching elements found.')).toBeInTheDocument();
});
it('should handle multiple checkbox selection', () => {
const onCheckedChange = jest.fn();
render(<CheckboxFilter options={options} onCheckedChange={onCheckedChange} />);
render(
<CheckboxFilter currentOptions={[]} options={options} onCheckedChange={onCheckedChange} />,
);
const checkbox1 = screen.getByLabelText('Option 1');
const checkbox2 = screen.getByLabelText('Option 2');
......@@ -74,9 +98,33 @@ describe('CheckboxFilter - component', () => {
{ id: '2', label: 'Option 2' },
]);
});
it('should handle multiple change of radio selection', () => {
const onCheckedChange = jest.fn();
render(
<CheckboxFilter
currentOptions={[]}
options={options}
onCheckedChange={onCheckedChange}
type="radio"
/>,
);
const checkbox1 = screen.getByLabelText('Option 1');
const checkbox2 = screen.getByLabelText('Option 2');
fireEvent.click(checkbox1);
expect(onCheckedChange).toHaveBeenCalledWith([{ id: '1', label: 'Option 1' }]);
fireEvent.click(checkbox2);
expect(onCheckedChange).toHaveBeenCalledWith([{ id: '2', label: 'Option 2' }]);
});
it('should handle unchecking a checkbox', () => {
const onCheckedChange = jest.fn();
render(<CheckboxFilter options={options} onCheckedChange={onCheckedChange} />);
render(
<CheckboxFilter currentOptions={[]} options={options} onCheckedChange={onCheckedChange} />,
);
const checkbox = screen.getByLabelText('Option 1');
......@@ -86,19 +134,19 @@ describe('CheckboxFilter - component', () => {
expect(onCheckedChange).toHaveBeenCalledWith([]);
});
it('should render search input when isSearchEnabled is true', () => {
render(<CheckboxFilter options={options} />);
render(<CheckboxFilter currentOptions={[]} options={options} />);
const searchInput = screen.getByLabelText('search-input');
expect(searchInput).toBeInTheDocument();
});
it('should not render search input when isSearchEnabled is false', () => {
render(<CheckboxFilter options={options} isSearchEnabled={false} />);
render(<CheckboxFilter currentOptions={[]} options={options} isSearchEnabled={false} />);
const searchInput = screen.queryByLabelText('search-input');
expect(searchInput).not.toBeInTheDocument();
});
it('should not filter options based on search input when isSearchEnabled is false', () => {
render(<CheckboxFilter options={options} isSearchEnabled={false} />);
render(<CheckboxFilter currentOptions={[]} options={options} isSearchEnabled={false} />);
const searchInput = screen.queryByLabelText('search-input');
expect(searchInput).not.toBeInTheDocument();
options.forEach(option => {
......@@ -106,4 +154,15 @@ describe('CheckboxFilter - component', () => {
expect(checkboxLabel).toBeInTheDocument();
});
});
it('should set checked param based on currentOptions prop', async () => {
render(<CheckboxFilter options={options} currentOptions={currentOptions} />);
const option1: HTMLInputElement = screen.getByLabelText('Option 1');
const option2: HTMLInputElement = screen.getByLabelText('Option 2');
const option3: HTMLInputElement = screen.getByLabelText('Option 3');
expect(option1.checked).toBe(false);
expect(option2.checked).toBe(true);
expect(option3.checked).toBe(false);
});
});
/* eslint-disable no-magic-numbers */
import lensIcon from '@/assets/vectors/icons/lens.svg';
import Image from 'next/image';
import React, { useEffect, useState } from 'react';
import lensIcon from '@/assets/vectors/icons/lens.svg';
import { twMerge } from 'tailwind-merge';
export type CheckboxItem = { id: string; label: string };
import { CheckboxItem } from './CheckboxFilter.types';
import { OptionInput } from './OptionInput';
type CheckboxFilterProps = {
options: CheckboxItem[];
currentOptions: CheckboxItem[];
onFilterChange?: (filteredItems: CheckboxItem[]) => void;
onCheckedChange?: (filteredItems: CheckboxItem[]) => void;
isSearchEnabled?: boolean;
type?: 'checkbox' | 'radio';
};
export const CheckboxFilter = ({
options,
currentOptions = [],
onFilterChange,
onCheckedChange,
isSearchEnabled = true,
type = 'checkbox',
}: CheckboxFilterProps): React.ReactNode => {
const [searchTerm, setSearchTerm] = useState('');
const [filteredOptions, setFilteredOptions] = useState<CheckboxItem[]>(options);
......@@ -39,14 +43,19 @@ export const CheckboxFilter = ({
};
const handleCheckboxChange = (option: CheckboxItem): void => {
const newCheckedCheckboxes = checkedCheckboxes.includes(option)
? checkedCheckboxes.filter(item => item !== option)
const newCheckedCheckboxes = checkedCheckboxes.some(item => item.id === option.id)
? checkedCheckboxes.filter(item => item.id !== option.id)
: [...checkedCheckboxes, option];
setCheckedCheckboxes(newCheckedCheckboxes);
onCheckedChange?.(newCheckedCheckboxes);
};
const handleRadioChange = (option: CheckboxItem): void => {
setCheckedCheckboxes([option]);
onCheckedChange?.([option]);
};
useEffect(() => {
setFilteredOptions(options);
}, [options]);
......@@ -86,15 +95,13 @@ export const CheckboxFilter = ({
<ul className="columns-2 gap-8">
{filteredOptions.map(option => (
<li key={option.id} className="mb-5 flex items-center gap-x-2">
<input
type="checkbox"
id={option.id}
className=" h-4 w-4 shrink-0 accent-primary-500"
onChange={(): void => handleCheckboxChange(option)}
<OptionInput
option={option}
currentOptions={currentOptions}
handleRadioChange={handleRadioChange}
handleCheckboxChange={handleCheckboxChange}
type={type}
/>
<label htmlFor={option.id} className="break-all text-sm">
{option.label}
</label>
</li>
))}
</ul>
......
export type CheckboxItem = { id: string; label: string };
import { twMerge } from 'tailwind-merge';
import { CheckboxItem } from '../CheckboxFilter.types';
interface Props {
option: CheckboxItem;
currentOptions: CheckboxItem[];
type: 'checkbox' | 'radio';
handleCheckboxChange(option: CheckboxItem): void;
handleRadioChange(option: CheckboxItem): void;
}
export const OptionInput = ({
option,
currentOptions = [],
type,
handleCheckboxChange,
handleRadioChange,
}: Props): React.ReactNode => {
const isChecked = Boolean(currentOptions.find(currentOption => currentOption.id === option.id));
const handleChange = (): void => {
switch (type) {
case 'checkbox':
handleCheckboxChange(option);
break;
case 'radio':
handleRadioChange(option);
break;
default:
throw new Error(`${type} is unknown option input type`);
}
};
return (
<label className="flex items-center gap-x-2">
<input
type={type}
className={twMerge(
'h-4 w-4 shrink-0 accent-primary-500',
type === 'radio' && 'rounded-full',
)}
onChange={handleChange}
checked={isChecked}
/>
<div className="break-all text-sm">{option.label}</div>
</label>
);
};
export { OptionInput } from './OptionInput.component';
/* eslint-disable no-magic-numbers */
import { compartmentPathwaysDetailsFixture } from '@/models/fixtures/compartmentPathways';
import { configurationFixture } from '@/models/fixtures/configurationFixture';
import { modelsFixture } from '@/models/fixtures/modelsFixture';
import { statisticsFixture } from '@/models/fixtures/statisticsFixture';
import { apiPath } from '@/redux/apiPath';
import { CONFIGURATION_INITIAL_STORE_MOCK } from '@/redux/configuration/configuration.mock';
import { INITIAL_STORE_STATE_MOCK } from '@/redux/root/root.fixtures';
import { AppDispatch, RootState } from '@/redux/store';
import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse';
import { getReduxStoreWithActionsListener } from '@/utils/testing/getReduxStoreActionsListener';
import { InitialStoreState } from '@/utils/testing/getReduxWrapperWithStore';
import { render, screen } from '@testing-library/react';
import { HttpStatusCode } from 'axios';
import { act } from 'react-dom/test-utils';
import { MockStoreEnhanced } from 'redux-mock-store';
import { ELEMENTS_COLUMNS } from '../ExportCompound/ExportCompound.constant';
import { Elements } from './Elements.component';
const mockedAxiosClient = mockNetworkNewAPIResponse();
const renderComponent = (
initialStore?: InitialStoreState,
): { store: MockStoreEnhanced<Partial<RootState>, AppDispatch> } => {
const { Wrapper, store } = getReduxStoreWithActionsListener(initialStore);
return (
render(
<Wrapper>
<Elements />
</Wrapper>,
),
{
store,
}
);
};
describe('Elements - component', () => {
it('should render all elements sections', () => {
renderComponent({
...INITIAL_STORE_STATE_MOCK,
configuration: {
...CONFIGURATION_INITIAL_STORE_MOCK,
main: {
...CONFIGURATION_INITIAL_STORE_MOCK.main,
data: {
...configurationFixture,
miriamTypes: {
compartment_label: {
commonName: 'Compartment',
homepage: '',
registryIdentifier: '',
uris: [''],
},
},
},
},
},
statistics: {
data: {
...statisticsFixture,
elementAnnotations: {
compartment_label: 1,
pathway: 0,
},
},
loading: 'succeeded',
error: {
message: '',
name: '',
},
},
compartmentPathways: {
data: compartmentPathwaysDetailsFixture,
loading: 'succeeded',
error: {
message: '',
name: '',
},
},
});
const annotations = screen.getByText('Select annotations');
const includedCompartmentPathways = screen.getByText('Select included compartment / pathways');
const excludedCompartmentPathways = screen.getByText('Select excluded compartment / pathways');
const downloadButton = screen.getByText('Download');
expect(annotations).toBeVisible();
expect(includedCompartmentPathways).toBeVisible();
expect(excludedCompartmentPathways).toBeVisible();
expect(downloadButton).toBeVisible();
});
it('should handle download button click and dispatch proper data', async () => {
mockedAxiosClient.onPost(apiPath.downloadElementsCsv()).reply(HttpStatusCode.Ok, 'test');
const FIRST_COMPARMENT_PATHWAY_NAME = compartmentPathwaysDetailsFixture[0].name;
const FIRST_COMPARMENT_PATHWAY_ID = compartmentPathwaysDetailsFixture[0].id;
const SECOND_COMPARMENT_PATHWAY_NAME = compartmentPathwaysDetailsFixture[1].name;
const SECOND_COMPARMENT_PATHWAY_ID = compartmentPathwaysDetailsFixture[1].id;
const { store } = renderComponent({
...INITIAL_STORE_STATE_MOCK,
configuration: {
...CONFIGURATION_INITIAL_STORE_MOCK,
main: {
...CONFIGURATION_INITIAL_STORE_MOCK.main,
data: {
...configurationFixture,
miriamTypes: {
compartment_label: {
commonName: 'Compartment',
homepage: '',
registryIdentifier: '',
uris: [''],
},
},
},
},
},
statistics: {
data: {
...statisticsFixture,
elementAnnotations: {
compartment_label: 1,
pathway: 0,
},
},
loading: 'succeeded',
error: {
message: '',
name: '',
},
},
compartmentPathways: {
data: compartmentPathwaysDetailsFixture,
loading: 'succeeded',
error: {
message: '',
name: '',
},
},
models: {
data: modelsFixture,
loading: 'succeeded',
error: {
message: '',
name: '',
},
},
});
const annotations = screen.getByText('Select annotations');
await act(() => {
annotations.click();
});
const annotationInput = screen.getByLabelText('Compartment');
await act(() => {
annotationInput.click();
});
expect(annotationInput).toBeChecked();
const includedCompartmentPathways = screen.getByText('Select included compartment / pathways');
await act(() => {
includedCompartmentPathways.click();
});
const includedCompartmentPathwaysInput = screen.getAllByLabelText(
FIRST_COMPARMENT_PATHWAY_NAME,
)[0];
await act(() => {
includedCompartmentPathwaysInput.click();
});
expect(includedCompartmentPathwaysInput).toBeChecked();
const excludedCompartmentPathways = screen.getByText('Select excluded compartment / pathways');
await act(() => {
excludedCompartmentPathways.click();
});
const excludedCompartmentPathwaysInput = screen.getAllByLabelText(
SECOND_COMPARMENT_PATHWAY_NAME,
)[1];
await act(() => {
excludedCompartmentPathwaysInput.click();
});
expect(excludedCompartmentPathwaysInput).toBeChecked();
const downloadButton = screen.getByText('Download');
await act(() => {
downloadButton.click();
});
const actions = store.getActions();
const firstAction = actions[0];
expect(firstAction.meta.arg).toEqual({
columns: ELEMENTS_COLUMNS,
submaps: modelsFixture.map(item => item.idObject),
annotations: ['compartment_label'],
includedCompartmentIds: [FIRST_COMPARMENT_PATHWAY_ID],
excludedCompartmentIds: [SECOND_COMPARMENT_PATHWAY_ID],
});
});
});
import { Export } from '../ExportCompound';
import { ANNOTATIONS_TYPE } from '../ExportCompound/ExportCompound.constant';
export const Elements = (): React.ReactNode => {
return (
<div data-testid="elements-tab">
<Export>
<Export.Types />
<Export.Columns />
<Export.Annotations />
<Export.Annotations type={ANNOTATIONS_TYPE.ELEMENTS} />
<Export.IncludedCompartmentPathways />
<Export.ExcludedCompartmentPathways />
<Export.DownloadElements />
......
......@@ -6,7 +6,10 @@ import {
import { StoreType } from '@/redux/store';
import { statisticsFixture } from '@/models/fixtures/statisticsFixture';
import { act } from 'react-dom/test-utils';
import { CONFIGURATION_INITIAL_STORE_MOCK } from '@/redux/configuration/configuration.mock';
import { configurationFixture } from '@/models/fixtures/configurationFixture';
import { Annotations } from './Annotations.component';
import { ANNOTATIONS_TYPE } from '../ExportCompound.constant';
const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState);
......@@ -14,7 +17,7 @@ const renderComponent = (initialStoreState: InitialStoreState = {}): { store: St
return (
render(
<Wrapper>
<Annotations />
<Annotations type={ANNOTATIONS_TYPE.ELEMENTS} />
</Wrapper>,
),
{
......@@ -26,11 +29,28 @@ const renderComponent = (initialStoreState: InitialStoreState = {}): { store: St
describe('Annotations - component', () => {
it('should display annotations checkboxes when fetching data is successful', async () => {
renderComponent({
configuration: {
...CONFIGURATION_INITIAL_STORE_MOCK,
main: {
...CONFIGURATION_INITIAL_STORE_MOCK.main,
data: {
...configurationFixture,
miriamTypes: {
compartment_label: {
commonName: 'Compartment',
homepage: '',
registryIdentifier: '',
uris: [''],
},
},
},
},
},
statistics: {
data: {
...statisticsFixture,
elementAnnotations: {
compartment: 1,
compartment_label: 1,
pathway: 0,
},
},
......@@ -54,7 +74,7 @@ describe('Annotations - component', () => {
await waitFor(() => {
expect(screen.getByTestId('checkbox-filter')).toBeInTheDocument();
expect(screen.getByLabelText('compartment')).toBeInTheDocument();
expect(screen.getByLabelText('Compartment')).toBeInTheDocument();
expect(screen.getByLabelText('search-input')).toBeInTheDocument();
});
});
......
import { useContext } from 'react';
import { ZERO } from '@/constants/common';
import { miramiTypesSelector } from '@/redux/configuration/configuration.selectors';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import {
elementAnnotationsSelector,
loadingStatisticsSelector,
statisticsDataSelector,
} from '@/redux/statistics/statistics.selectors';
import { ZERO } from '@/constants/common';
import { useContext } from 'react';
import { CheckboxFilter } from '../../CheckboxFilter';
import { CollapsibleSection } from '../../CollapsibleSection';
import { ExportContext } from '../ExportCompound.context';
import { AnnotationsType } from './Annotations.types';
import { getAnnotationsCheckboxElements } from './Annotations.utils';
export const Annotations = (): React.ReactNode => {
const { setAnnotations } = useContext(ExportContext);
type AnnotationsProps = {
type: AnnotationsType;
};
export const Annotations = ({ type }: AnnotationsProps): React.ReactNode => {
const { setAnnotations, data } = useContext(ExportContext);
const currentAnnotations = data.annotations;
const loadingStatistics = useAppSelector(loadingStatisticsSelector);
const elementAnnotations = useAppSelector(elementAnnotationsSelector);
const statistics = useAppSelector(statisticsDataSelector);
const miramiTypes = useAppSelector(miramiTypesSelector);
const isPending = loadingStatistics === 'pending';
const mappedElementAnnotations = elementAnnotations
? Object.keys(elementAnnotations)?.map(el => ({ id: el, label: el }))
: [];
const checkboxElements = getAnnotationsCheckboxElements({ type, statistics, miramiTypes });
return (
<CollapsibleSection title="Select annotations">
{isPending && <p>Loading...</p>}
{!isPending && mappedElementAnnotations && mappedElementAnnotations.length > ZERO && (
<CheckboxFilter options={mappedElementAnnotations} onCheckedChange={setAnnotations} />
{!isPending && checkboxElements && checkboxElements.length > ZERO && (
<CheckboxFilter
options={checkboxElements}
currentOptions={currentAnnotations}
onCheckedChange={setAnnotations}
/>
)}
</CollapsibleSection>
);
......
import { ANNOTATIONS_TYPE } from '../ExportCompound.constant';
export type AnnotationsType = (typeof ANNOTATIONS_TYPE)[keyof typeof ANNOTATIONS_TYPE];