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

feat: add submap download comp wo tests

parent e461460d
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...,!106feat: add submap download component (MIN-122)
Pipeline #84267 failed
Showing
with 318 additions and 7 deletions
import { formatsHandlersSelector } from '@/redux/configuration/configuration.selectors';
import { Button } from '@/shared/Button';
import { useSelect } from 'downshift';
import { useSelector } from 'react-redux';
import { SUBMAP_DOWNLOAD_HANDLERS_NAMES } from './DownloadSubmap.constants';
import { useGetSubmapDownloadUrl } from './utils/useGetSubmapDownloadUrl';
export const DownloadSubmap = (): JSX.Element => {
const formatsHandlers = useSelector(formatsHandlersSelector);
const formatsHandlersItems = Object.entries(formatsHandlers);
const getSubmapDownloadUrl = useGetSubmapDownloadUrl();
const { isOpen, getToggleButtonProps, getMenuProps } = useSelect({
items: formatsHandlersItems,
});
return (
<div className="relative">
<Button variantStyles="ghost" className="mr-4" {...getToggleButtonProps()}>
Download
</Button>
<ul
className={`absolute left-[-50%] z-10 max-h-80 w-48 overflow-scroll rounded-sm border bg-white p-0 ps-0 ${
!isOpen && 'hidden'
}`}
{...getMenuProps()}
>
{isOpen &&
formatsHandlersItems.map(([formatId, handler]) => (
<li key={formatId}>
<a
className="flex flex-col border-t px-4 py-2 shadow-sm"
href={getSubmapDownloadUrl({ handler })}
target="_blank"
download
>
<span>{SUBMAP_DOWNLOAD_HANDLERS_NAMES[formatId]}</span>
</a>
</li>
))}
</ul>
</div>
);
};
import {
CELL_DESIGNER_SBML_HANDLER_NAME_ID,
GPML_HANDLER_NAME_ID,
SBGN_ML_HANDLER_NAME_ID,
SBML_HANDLER_NAME_ID,
} from '@/redux/configuration/configuration.constants';
export const SUBMAP_DOWNLOAD_HANDLERS_NAMES: Record<string, string> = {
[GPML_HANDLER_NAME_ID]: 'GPML',
[SBML_HANDLER_NAME_ID]: 'SBML',
[CELL_DESIGNER_SBML_HANDLER_NAME_ID]: 'CellDesigner SBML',
[SBGN_ML_HANDLER_NAME_ID]: 'SBGN-ML',
};
export { DownloadSubmap } from './DownloadSubmap.component';
import { BASE_API_URL, PROJECT_ID } from '@/constants';
import { currentBackgroundSelector } from '@/redux/backgrounds/background.selectors';
import { mapDataSizeSelector } from '@/redux/map/map.selectors';
import { currentModelSelector } from '@/redux/models/models.selectors';
import { useSelector } from 'react-redux';
type GetSubmapDownloadUrl = ({ handler }: { handler: string }) => string;
export const useGetSubmapDownloadUrl = (): GetSubmapDownloadUrl => {
const model = useSelector(currentModelSelector);
const background = useSelector(currentBackgroundSelector);
const mapSize = useSelector(mapDataSizeSelector);
const getSubmapDownloadUrl: GetSubmapDownloadUrl = ({ handler }) => {
return `${BASE_API_URL}/projects/${PROJECT_ID}/models/${model?.idObject}:downloadModel?backgroundOverlayId=${background?.id}&handlerClass=${handler}&zoomLevel=${mapSize.maxZoom}`;
};
return getSubmapDownloadUrl;
};
import { Button } from '@/shared/Button';
import { IconButton } from '@/shared/IconButton';
import { DownloadSubmap } from './DownloadSubmap';
interface SubmapItemProps {
modelName: string;
......@@ -10,9 +10,7 @@ export const SubmpamItem = ({ modelName, onOpenClick }: SubmapItemProps): JSX.El
<div className="flex flex-row flex-nowrap items-center justify-between border-b py-6">
{modelName}
<div className="flex flex-row flex-nowrap items-center">
<Button variantStyles="ghost" className="mr-4">
Download
</Button>
<DownloadSubmap />
<IconButton
icon="chevron-right"
className="h-6 w-6 bg-white-pearl"
......
import { z } from 'zod';
export const elementTypeSchema = z.object({
className: z.string(),
name: z.string(),
parentClass: z.string(),
});
export const optionSchema = z.object({
idObject: z.number(),
type: z.string(),
valueType: z.string(),
commonName: z.string(),
isServerSide: z.boolean(),
group: z.string(),
value: z.string().optional(),
});
export const formatSchema = z.object({
name: z.string(),
handler: z.string(),
extension: z.string(),
});
export const overlayTypeSchema = z.object({ name: z.string() });
export const reactionTypeSchema = z.object({
className: z.string(),
name: z.string(),
parentClass: z.string(),
});
export const miriamTypesSchema = z.record(
z.string(),
z.object({
commonName: z.string(),
homepage: z.string().nullable(),
registryIdentifier: z.string().nullable(),
uris: z.array(z.string()),
}),
);
export const bioEntityFieldSchema = z.object({ commonName: z.string(), name: z.string() });
export const annotatorSchema = z.object({
className: z.string(),
name: z.string(),
description: z.string(),
url: z.string(),
elementClassNames: z.array(z.string()),
parameters: z.array(
z.object({
field: z.string().nullable().optional(),
annotation_type: z.string().nullable().optional(),
order: z.number(),
type: z.string(),
}),
),
});
export const privilegeTypeSchema = z.record(
z.string(),
z.object({
commonName: z.string(),
objectType: z.string().nullable(),
valueType: z.string(),
}),
);
export const mapTypeSchema = z.object({ name: z.string(), id: z.string() });
export const mapCanvasTypeSchema = z.object({ name: z.string(), id: z.string() });
export const unitTypeSchema = z.object({ name: z.string(), id: z.string() });
export const modificationStateTypeSchema = z.record(
z.string(),
z.object({ commonName: z.string(), abbreviation: z.string() }),
);
export const configurationSchema = z.object({
elementTypes: z.array(elementTypeSchema),
options: z.array(optionSchema),
imageFormats: z.array(formatSchema),
modelFormats: z.array(formatSchema),
overlayTypes: z.array(overlayTypeSchema),
reactionTypes: z.array(reactionTypeSchema),
miriamTypes: miriamTypesSchema,
bioEntityFields: z.array(bioEntityFieldSchema),
version: z.string(),
buildDate: z.string(),
gitHash: z.string(),
annotators: z.array(annotatorSchema),
privilegeTypes: privilegeTypeSchema,
mapTypes: z.array(mapTypeSchema),
mapCanvasTypes: z.array(mapCanvasTypeSchema),
unitTypes: z.array(unitTypeSchema),
modificationStateTypes: modificationStateTypeSchema,
});
import { ConfigurationFormatSchema } from '@/types/models';
export const CONFIGURATION_FORMATS_TYPES_MOCK: string[] = [
'PNG image',
'PDF',
'SVG image',
'CellDesigner SBML',
'SBGN-ML',
'SBML',
'GPML',
];
export const CONFIGURATION_FORMATS_COLOURS_MOCK: ConfigurationFormatSchema[] = [
{
name: 'PNG image',
handler: 'lcsb.mapviewer.converter.graphics.PngImageGenerator',
extension: 'png',
},
{
name: 'PDF',
handler: 'lcsb.mapviewer.converter.graphics.PdfImageGenerator',
extension: 'pdf',
},
{
name: 'SVG image',
handler: 'lcsb.mapviewer.converter.graphics.SvgImageGenerator',
extension: 'svg',
},
{
name: 'CellDesigner SBML',
handler: 'lcsb.mapviewer.converter.model.celldesigner.CellDesignerXmlParser',
extension: 'xml',
},
{
name: 'SBGN-ML',
handler: 'lcsb.mapviewer.converter.model.sbgnml.SbgnmlXmlConverter',
extension: 'sbgn',
},
{
name: 'SBML',
handler: 'lcsb.mapviewer.converter.model.sbml.SbmlParser',
extension: 'xml',
},
{
name: 'GPML',
handler: 'lcsb.mapviewer.wikipathway.GpmlParser',
extension: 'gpml',
},
];
......@@ -3,3 +3,15 @@ export const MAX_COLOR_VAL_NAME_ID = 'MAX_COLOR_VAL';
export const SIMPLE_COLOR_VAL_NAME_ID = 'SIMPLE_COLOR_VAL';
export const NEUTRAL_COLOR_VAL_NAME_ID = 'NEUTRAL_COLOR_VAL';
export const OVERLAY_OPACITY_NAME_ID = 'OVERLAY_OPACITY';
export const LEGEND_FILE_NAMES_IDS = [
'LEGEND_FILE_1',
'LEGEND_FILE_2',
'LEGEND_FILE_3',
'LEGEND_FILE_4',
];
export const GPML_HANDLER_NAME_ID = 'GPML';
export const SBML_HANDLER_NAME_ID = 'SBML';
export const CELL_DESIGNER_SBML_HANDLER_NAME_ID = 'CellDesigner SBML';
export const SBGN_ML_HANDLER_NAME_ID = 'SBGN-ML';
/* eslint-disable no-magic-numbers */
import { DEFAULT_ERROR } from '@/constants/errors';
import {
CONFIGURATION_OPTIONS_TYPES_MOCK,
CONFIGURATION_OPTIONS_COLOURS_MOCK,
CONFIGURATION_OPTIONS_TYPES_MOCK,
} from '@/models/mocks/configurationOptionMock';
import { ConfigurationState } from './configuration.adapter';
......
import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
import { getConfigurationOptions } from './configuration.thunks';
import { ConfigurationState, configurationAdapter } from './configuration.adapter';
import { getConfigurationOptions } from './configuration.thunks';
export const getConfigurationOptionsReducer = (
builder: ActionReducerMapBuilder<ConfigurationState>,
......
import { ConfigurationFormatSchema } from '@/types/models';
import { createSelector } from '@reduxjs/toolkit';
import { configurationAdapter } from './configuration.adapter';
import { rootSelector } from '../root/root.selectors';
import { configurationAdapter } from './configuration.adapter';
import {
CELL_DESIGNER_SBML_HANDLER_NAME_ID,
GPML_HANDLER_NAME_ID,
LEGEND_FILE_NAMES_IDS,
MAX_COLOR_VAL_NAME_ID,
MIN_COLOR_VAL_NAME_ID,
NEUTRAL_COLOR_VAL_NAME_ID,
OVERLAY_OPACITY_NAME_ID,
SBGN_ML_HANDLER_NAME_ID,
SBML_HANDLER_NAME_ID,
SIMPLE_COLOR_VAL_NAME_ID,
} from './configuration.constants';
import { ConfigurationHandlersIds } from './configuration.types';
const configurationSelector = createSelector(rootSelector, state => state.configuration);
const configurationOptionsSelector = createSelector(configurationSelector, state => state.options);
const configurationMainSelector = createSelector(configurationSelector, state => state.main.data);
const configurationAdapterSelectors = configurationAdapter.getSelectors();
......@@ -37,3 +46,43 @@ export const simpleColorValSelector = createSelector(
configurationSelector,
state => configurationAdapterSelectors.selectById(state, SIMPLE_COLOR_VAL_NAME_ID)?.value,
);
export const defaultLegendImagesSelector = createSelector(configurationOptionsSelector, state =>
LEGEND_FILE_NAMES_IDS.map(
legendNameId => configurationAdapterSelectors.selectById(state, legendNameId)?.value,
).filter(legendImage => Boolean(legendImage)),
);
export const elementTypesSelector = createSelector(
configurationMainSelector,
state => state?.elementTypes,
);
export const modelFormatsSelector = createSelector(
configurationMainSelector,
state => state?.modelFormats,
);
export const formatsEntriesSelector = createSelector(
modelFormatsSelector,
(modelFormats): Record<string, ConfigurationFormatSchema> => {
return Object.fromEntries(
modelFormats
.flat()
.filter((format): format is ConfigurationFormatSchema => Boolean(format))
.map(format => [format.name, format]),
);
},
);
export const formatsHandlersSelector = createSelector(
formatsEntriesSelector,
(formats): ConfigurationHandlersIds => {
return {
[GPML_HANDLER_NAME_ID]: formats[GPML_HANDLER_NAME_ID]?.handler,
[SBML_HANDLER_NAME_ID]: formats[SBML_HANDLER_NAME_ID]?.handler,
[CELL_DESIGNER_SBML_HANDLER_NAME_ID]: formats[CELL_DESIGNER_SBML_HANDLER_NAME_ID]?.handler,
[SBGN_ML_HANDLER_NAME_ID]: formats[SBGN_ML_HANDLER_NAME_ID]?.handler,
};
},
);
import { FetchDataState } from '@/types/fetchDataState';
import { Configuration } from '@/types/models';
import {
CELL_DESIGNER_SBML_HANDLER_NAME_ID,
GPML_HANDLER_NAME_ID,
SBGN_ML_HANDLER_NAME_ID,
SBML_HANDLER_NAME_ID,
} from './configuration.constants';
export type ConfigurationMainState = FetchDataState<Configuration>;
export interface ConfigurationHandlersIds {
[GPML_HANDLER_NAME_ID]?: string;
[SBML_HANDLER_NAME_ID]?: string;
[CELL_DESIGNER_SBML_HANDLER_NAME_ID]?: string;
[SBGN_ML_HANDLER_NAME_ID]?: string;
}
......@@ -2,6 +2,7 @@ import { rootSelector } from '@/redux/root/root.selectors';
import { createSelector } from '@reduxjs/toolkit';
import { MODEL_ID_DEFAULT } from '../map/map.constants';
import { mapDataSelector } from '../map/map.selectors';
import { overlaysDataSelector } from '../overlays/overlays.selectors';
export const modelsSelector = createSelector(rootSelector, state => state.models);
......@@ -13,6 +14,12 @@ export const currentModelSelector = createSelector(
(models, mapData) => models.find(model => model.idObject === mapData.modelId),
);
export const currentOverlaySelector = createSelector(
overlaysDataSelector,
mapDataSelector,
(models, mapData) => models.find(model => model.idObject === mapData.overlaysIds),
);
export const modelsIdsSelector = createSelector(modelsDataSelector, models =>
models.map(model => model.idObject),
);
......
......@@ -4,6 +4,7 @@ import { bioEntitySchema } from '@/models/bioEntitySchema';
import { chemicalSchema } from '@/models/chemicalSchema';
import { colorSchema } from '@/models/colorSchema';
import { configurationOptionSchema } from '@/models/configurationOptionSchema';
import { configurationSchema, formatSchema } from '@/models/configurationSchema';
import { disease } from '@/models/disease';
import { drugSchema } from '@/models/drugSchema';
import { elementSearchResult, elementSearchResultType } from '@/models/elementSearchResult';
......@@ -51,5 +52,7 @@ export type ElementSearchResultType = z.infer<typeof elementSearchResultType>;
export type SessionValid = z.infer<typeof sessionSchemaValid>;
export type Login = z.infer<typeof loginSchema>;
export type ConfigurationOption = z.infer<typeof configurationOptionSchema>;
export type Configuration = z.infer<typeof configurationSchema>;
export type ConfigurationFormatSchema = z.infer<typeof formatSchema>;
export type OverlayBioEntity = z.infer<typeof overlayBioEntitySchema>;
export type Color = z.infer<typeof colorSchema>;
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