@cccsaurora/howler-ui 2.19.0-dev.1085 → 2.19.0-dev.1099
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/elements/hit/HitLabels.test.d.ts +1 -0
- package/components/elements/hit/HitLabels.test.js +77 -0
- package/components/elements/hit/HitLinks.d.ts +5 -4
- package/components/elements/hit/HitLinks.js +28 -10
- package/components/elements/hit/HitNotebooks.js +23 -22
- package/components/elements/hit/NotebookTooltip.d.ts +3 -0
- package/components/elements/hit/NotebookTooltip.js +8 -0
- package/components/elements/hit/PivotTooltip.d.ts +7 -0
- package/components/elements/hit/PivotTooltip.js +9 -0
- package/components/elements/hit/RelatedLinkTooltip.d.ts +6 -0
- package/components/elements/hit/RelatedLinkTooltip.js +8 -0
- package/components/elements/hit/ResolvePivotUrl.d.ts +4 -0
- package/components/elements/hit/ResolvePivotUrl.js +22 -0
- package/components/elements/hit/related/PivotLink.d.ts +3 -0
- package/components/elements/hit/related/PivotLink.js +5 -2
- package/components/elements/hit/related/RelatedLink.d.ts +1 -0
- package/components/elements/hit/related/RelatedLink.js +18 -15
- package/locales/en/translation.json +5 -0
- package/locales/fr/translation.json +5 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import HitLabels from './HitLabels';
|
|
5
|
+
/**
|
|
6
|
+
* HitLabels.test.tsx
|
|
7
|
+
*
|
|
8
|
+
* Purpose:
|
|
9
|
+
* This test suite validates the core behavior of the HitLabels component.
|
|
10
|
+
* - Correct transformation of hit.howler.labels into UI chips
|
|
11
|
+
* - Proper rendering of all label values
|
|
12
|
+
* - Conditional display of the edit button based on readOnly mode
|
|
13
|
+
* - Safe rendering when no labels are present
|
|
14
|
+
*/
|
|
15
|
+
vi.mock('components/hooks/useMyApi', () => ({
|
|
16
|
+
default: () => ({
|
|
17
|
+
dispatchApi: vi.fn()
|
|
18
|
+
})
|
|
19
|
+
}));
|
|
20
|
+
vi.mock('components/app/providers/HitProvider', () => ({
|
|
21
|
+
HitContext: {}
|
|
22
|
+
}));
|
|
23
|
+
vi.mock('use-context-selector', () => ({
|
|
24
|
+
useContextSelector: () => vi.fn()
|
|
25
|
+
}));
|
|
26
|
+
vi.mock('react-i18next', () => ({
|
|
27
|
+
useTranslation: () => ({
|
|
28
|
+
t: (key) => key
|
|
29
|
+
}),
|
|
30
|
+
Trans: ({ i18nKey }) => i18nKey
|
|
31
|
+
}));
|
|
32
|
+
vi.mock('utils/constants', () => ({
|
|
33
|
+
LABEL_TYPES: {
|
|
34
|
+
security: { icon: null, color: '#ff0000' },
|
|
35
|
+
system: { icon: null, color: '#00ff00' }
|
|
36
|
+
}
|
|
37
|
+
}));
|
|
38
|
+
const baseHit = {
|
|
39
|
+
howler: {
|
|
40
|
+
id: 'hit-1',
|
|
41
|
+
labels: {
|
|
42
|
+
security: ['critical', 'high'],
|
|
43
|
+
system: ['info']
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
describe('HitLabels', () => {
|
|
48
|
+
it('renders all labels from hit.howler.labels', () => {
|
|
49
|
+
render(_jsx(HitLabels, { hit: baseHit, readOnly: true }));
|
|
50
|
+
expect(screen.getByText('critical')).toBeInTheDocument();
|
|
51
|
+
expect(screen.getByText('high')).toBeInTheDocument();
|
|
52
|
+
expect(screen.getByText('info')).toBeInTheDocument();
|
|
53
|
+
});
|
|
54
|
+
it('renders correct number of labels', () => {
|
|
55
|
+
render(_jsx(HitLabels, { hit: baseHit, readOnly: true }));
|
|
56
|
+
const labels = screen.getAllByText(/critical|high|info/);
|
|
57
|
+
expect(labels.length).toBe(3);
|
|
58
|
+
});
|
|
59
|
+
it('shows edit button when not readOnly', () => {
|
|
60
|
+
render(_jsx(HitLabels, { hit: baseHit, readOnly: false }));
|
|
61
|
+
expect(screen.getByRole('button')).toBeInTheDocument();
|
|
62
|
+
});
|
|
63
|
+
it('hides edit button when readOnly', () => {
|
|
64
|
+
render(_jsx(HitLabels, { hit: baseHit, readOnly: true }));
|
|
65
|
+
expect(screen.queryByRole('button')).not.toBeInTheDocument();
|
|
66
|
+
});
|
|
67
|
+
it('renders without crashing when labels are empty', () => {
|
|
68
|
+
const emptyHit = {
|
|
69
|
+
howler: {
|
|
70
|
+
id: 'hit-1',
|
|
71
|
+
labels: {}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const { container } = render(_jsx(HitLabels, { hit: emptyHit, readOnly: true }));
|
|
75
|
+
expect(container).toBeInTheDocument();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import type { FC } from 'react';
|
|
1
2
|
import type { Analytic } from '@cccsaurora/howler-ui/models/entities/generated/Analytic';
|
|
2
3
|
import type { Dossier } from '@cccsaurora/howler-ui/models/entities/generated/Dossier';
|
|
3
4
|
import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
|
|
4
|
-
|
|
5
|
-
declare const HitLinks: FC<{
|
|
5
|
+
interface HitLinksProps {
|
|
6
6
|
hit?: Hit;
|
|
7
7
|
analytic?: Analytic;
|
|
8
|
-
dossiers
|
|
9
|
-
}
|
|
8
|
+
dossiers?: Dossier[];
|
|
9
|
+
}
|
|
10
|
+
declare const HitLinks: FC<HitLinksProps>;
|
|
10
11
|
export default HitLinks;
|
|
@@ -1,19 +1,37 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Grid, gridClasses } from '@mui/material';
|
|
3
|
+
import { sortBy, uniqBy } from 'lodash-es';
|
|
4
|
+
import { useMemo } from 'react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
3
6
|
import HitNotebooks from '@cccsaurora/howler-ui/components/elements/hit/HitNotebooks';
|
|
7
|
+
import ResolvePivotUrl from '@cccsaurora/howler-ui/components/elements/hit/ResolvePivotUrl';
|
|
4
8
|
import PivotLink from '@cccsaurora/howler-ui/components/elements/hit/related/PivotLink';
|
|
5
9
|
import RelatedLink from '@cccsaurora/howler-ui/components/elements/hit/related/RelatedLink';
|
|
6
|
-
import { sortBy, uniqBy } from 'lodash-es';
|
|
7
|
-
import { useTranslation } from 'react-i18next';
|
|
8
10
|
const HitLinks = ({ hit, analytic, dossiers = [] }) => {
|
|
9
11
|
const { i18n } = useTranslation();
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
dossiers.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
const displayLinks = useMemo(() => uniqBy(hit?.howler?.links ?? [], 'href').slice(0, 3), [hit?.howler?.links]);
|
|
13
|
+
const displayPivots = useMemo(() => {
|
|
14
|
+
const flattened = dossiers.flatMap(dossier => (dossier.pivots ?? []).map(pivot => {
|
|
15
|
+
const pivotUrl = pivot.format === 'link' ? ResolvePivotUrl(pivot, hit) : undefined;
|
|
16
|
+
return {
|
|
17
|
+
pivot,
|
|
18
|
+
dossier,
|
|
19
|
+
resolvedUrl: pivotUrl || `/dossier/${dossier.dossier_id}`
|
|
20
|
+
};
|
|
21
|
+
}));
|
|
22
|
+
return sortBy(flattened, item => item.pivot.label?.[i18n.language]);
|
|
23
|
+
}, [dossiers, i18n.language, hit]);
|
|
24
|
+
const hasNotebooks = (analytic?.notebooks?.length ?? 0) > 0;
|
|
25
|
+
if (displayLinks.length === 0 && displayPivots.length === 0 && !hasNotebooks) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
return (_jsxs(Grid, { container: true, spacing: 1, pr: 2, sx: { [`& .${gridClasses.item}`]: { display: 'flex' } }, children: [displayLinks
|
|
29
|
+
.filter(link => !!link.href)
|
|
30
|
+
.map(link => {
|
|
31
|
+
const safeTitle = link.title ?? link.href;
|
|
32
|
+
return (_jsx(Grid, { item: true, children: _jsx(RelatedLink, { compact: true, title: safeTitle, href: link.href, target: "_blank", rel: "noopener noreferrer" }) }, link.href));
|
|
33
|
+
}), displayPivots.map(({ pivot, dossier, resolvedUrl }) => {
|
|
34
|
+
return (_jsx(Grid, { item: true, children: _jsx(PivotLink, { pivot: pivot, hit: hit, dossier: dossier, resolvedUrl: resolvedUrl, compact: true }) }, `${dossier.dossier_id}-${pivot.value}`));
|
|
35
|
+
}), hasNotebooks && (_jsx(Grid, { item: true, children: _jsx(HitNotebooks, { analytic: analytic, hit: hit, compact: true }) }))] }));
|
|
18
36
|
};
|
|
19
37
|
export default HitLinks;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Avatar, Backdrop, Box, Button, Chip, CircularProgress, ClickAwayListener, Fade, MenuItem, Paper, Popper, Stack, TextField } from '@mui/material';
|
|
2
|
+
import { Avatar, Backdrop, Box, Button, Chip, CircularProgress, ClickAwayListener, Fade, MenuItem, Paper, Popper, Stack, TextField, Tooltip } from '@mui/material';
|
|
3
3
|
import api from '@cccsaurora/howler-ui/api';
|
|
4
4
|
import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
|
|
5
5
|
import { ModalContext } from '@cccsaurora/howler-ui/components/app/providers/ModalProvider';
|
|
6
|
+
import NotebookTooltip from '@cccsaurora/howler-ui/components/elements/hit/NotebookTooltip';
|
|
6
7
|
import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
7
8
|
import useMySnackbar from '@cccsaurora/howler-ui/components/hooks/useMySnackbar';
|
|
8
9
|
import { memo, useCallback, useContext, useEffect, useState } from 'react';
|
|
@@ -124,26 +125,26 @@ const HitNotebooks = ({ analytic, selectedNotebook, hit, compact }) => {
|
|
|
124
125
|
MenuProps: {
|
|
125
126
|
style: { zIndex: 35001 }
|
|
126
127
|
}
|
|
127
|
-
}, defaultValue: selectedNotebook ? analytic?.notebooks.find(n => n.name === selectedNotebook).value : '', children: [_jsx(MenuItem, { disabled: true, value: "", children: _jsx("em", { children: t('hit.notebook.select') }) }), analytic?.notebooks.sort(safeStringPropertyCompare('detection')).map(e => (_jsx(MenuItem, { value: e.value, children: _jsxs(Stack, { direction: 'row', sx: { width: '100%' }, children: [e.name, e.detection && (_jsxs(_Fragment, { children: [_jsx(Box, { flex: 1 }), _jsx(Chip, { label: e.detection, size: "small" })] }))] }) }, e.value)))] }), _jsx(Button, { variant: "outlined", disabled: loadedNotebook.name === '' || envs.length === 0, color: "success", onClick: () => checkJupyhub(), children: t('hit.notebook.goTo') })] })] }) })) }), _jsx(HowlerCard, { variant: compact ? 'outlined' : 'elevation', onClick: handleToggle, sx: [
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
128
|
+
}, defaultValue: selectedNotebook ? analytic?.notebooks.find(n => n.name === selectedNotebook).value : '', children: [_jsx(MenuItem, { disabled: true, value: "", children: _jsx("em", { children: t('hit.notebook.select') }) }), analytic?.notebooks.sort(safeStringPropertyCompare('detection')).map(e => (_jsx(MenuItem, { value: e.value, children: _jsxs(Stack, { direction: 'row', sx: { width: '100%' }, children: [e.name, e.detection && (_jsxs(_Fragment, { children: [_jsx(Box, { flex: 1 }), _jsx(Chip, { label: e.detection, size: "small" })] }))] }) }, e.value)))] }), _jsx(Button, { variant: "outlined", disabled: loadedNotebook.name === '' || envs.length === 0, color: "success", onClick: () => checkJupyhub(), children: t('hit.notebook.goTo') })] })] }) })) }), _jsx(Tooltip, { title: _jsx(NotebookTooltip, {}), children: _jsx("span", { children: _jsx(HowlerCard, { variant: compact ? 'outlined' : 'elevation', onClick: handleToggle, sx: [
|
|
129
|
+
theme => ({
|
|
130
|
+
cursor: 'pointer',
|
|
131
|
+
backgroundColor: 'transparent',
|
|
132
|
+
transition: theme.transitions.create(['border-color']),
|
|
133
|
+
'&:hover': { borderColor: 'primary.main', '& a': { textDecoration: 'underline' } },
|
|
134
|
+
'& > div': {
|
|
135
|
+
height: '100%'
|
|
136
|
+
},
|
|
137
|
+
'& a': { textDecoration: 'none', color: 'text.primary' }
|
|
138
|
+
}),
|
|
139
|
+
!compact && { border: 'thin solid', borderColor: 'transparent' }
|
|
140
|
+
], children: _jsxs(Stack, { direction: "row", p: 1, spacing: 1, alignItems: "center", children: [_jsx(Avatar, { variant: "rounded", src: '/images/jupyter_notebook_file_icon.png', sx: [
|
|
141
|
+
theme => ({
|
|
142
|
+
width: theme.spacing(3),
|
|
143
|
+
height: theme.spacing(3),
|
|
144
|
+
'& img': {
|
|
145
|
+
objectFit: 'contain'
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
], children: 'jupyter_notebook_file_icon.png' }), t('hit.notebook.tooltip')] }) }) }) })] }) })));
|
|
148
149
|
};
|
|
149
150
|
export default memo(HitNotebooks);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Typography } from '@mui/material';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
const NotebookTooltip = () => {
|
|
5
|
+
const { t } = useTranslation();
|
|
6
|
+
return (_jsxs(Box, { sx: { maxWidth: 300 }, children: [_jsx(Typography, { variant: "subtitle2", children: t('notebook.title') }), _jsx(Typography, { variant: "body2", color: "text.secondary", children: t('notebook.opens.panel') })] }));
|
|
7
|
+
};
|
|
8
|
+
export default NotebookTooltip;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Link, Typography } from '@mui/material';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
const PivotTooltip = ({ dossier, resolvedUrl }) => {
|
|
5
|
+
const dossierUrl = `/dossiers/${dossier.dossier_id}/edit?tab=leads&query=${encodeURIComponent(dossier.query)}`;
|
|
6
|
+
const { t } = useTranslation();
|
|
7
|
+
return (_jsxs(Box, { sx: { maxWidth: 300 }, children: [_jsx(Typography, { variant: "subtitle2", children: dossier.title }), _jsx(Typography, { variant: "body2", color: "text.secondary", children: dossier.owner }), _jsx(Typography, { variant: "body2", sx: { mt: 2 }, children: dossier.leads?.[0]?.content?.slice(0, 120) ?? t('pivot.description.none') }), _jsxs(Box, { sx: { mt: 2, wordBreak: 'break-all' }, children: [_jsx(Typography, { variant: "caption", display: "block", children: t('pivot.url') }), _jsx(Link, { href: resolvedUrl, target: "_blank", rel: "noopener noreferrer", underline: "hover", children: resolvedUrl })] }), _jsx(Box, { sx: { mt: 2 }, children: _jsx(Link, { href: dossierUrl, target: "_blank", rel: "noopener noreferrer", underline: "hover", children: t('pivot.dossier.open') }) })] }));
|
|
8
|
+
};
|
|
9
|
+
export default PivotTooltip;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Link, Typography } from '@mui/material';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
const RelatedLinkTooltip = ({ title, href }) => {
|
|
5
|
+
const { t } = useTranslation();
|
|
6
|
+
return (_jsxs(Box, { sx: { maxWidth: 300 }, children: [_jsx(Typography, { variant: "subtitle2", children: title }), _jsx(Box, { sx: { mt: 1, wordBreak: 'break-all' }, children: _jsx(Link, { href: href, target: "_blank", rel: "noopener noreferrer", underline: "hover", children: href }) }), _jsx(Box, { sx: { mt: 2 }, children: _jsx(Link, { href: href, target: "_blank", rel: "noopener noreferrer", underline: "hover", children: t('hit.header.link') }) })] }));
|
|
7
|
+
};
|
|
8
|
+
export default RelatedLinkTooltip;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Dossier } from '@cccsaurora/howler-ui/models/entities/generated/Dossier';
|
|
2
|
+
import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
|
|
3
|
+
declare const resolvePivotUrl: (pivot: NonNullable<Dossier["pivots"]>[number], currentHit?: Hit) => string;
|
|
4
|
+
export default resolvePivotUrl;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import Handlebars from 'handlebars';
|
|
2
|
+
import { flattenDeep } from '@cccsaurora/howler-ui/utils/utils';
|
|
3
|
+
const resolvePivotUrl = (pivot, currentHit) => {
|
|
4
|
+
const flatHit = flattenDeep(currentHit ?? {});
|
|
5
|
+
const templateObject = Object.fromEntries((pivot.mappings ?? []).map(mapping => {
|
|
6
|
+
const value = mapping.field === 'custom'
|
|
7
|
+
? mapping.custom_value
|
|
8
|
+
: Array.isArray(flatHit[mapping.field])
|
|
9
|
+
? flatHit[mapping.field][0]
|
|
10
|
+
: flatHit[mapping.field];
|
|
11
|
+
return [mapping.key, value];
|
|
12
|
+
}));
|
|
13
|
+
try {
|
|
14
|
+
return Handlebars.compile(pivot.value)(templateObject);
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
// eslint-disable-next-line no-console
|
|
18
|
+
console.error(`Failed to compile pivot template for value "${pivot.value}":`, e);
|
|
19
|
+
return pivot.value;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
export default resolvePivotUrl;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Dossier } from '@cccsaurora/howler-ui/models/entities/generated/Dossier';
|
|
1
2
|
import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
|
|
2
3
|
import type { Pivot } from '@cccsaurora/howler-ui/models/entities/generated/Pivot';
|
|
3
4
|
import { type FC } from 'react';
|
|
@@ -5,6 +6,8 @@ export interface PivotLinkProps {
|
|
|
5
6
|
pivot: Pivot;
|
|
6
7
|
hit: Hit;
|
|
7
8
|
compact?: boolean;
|
|
9
|
+
dossier: Dossier;
|
|
10
|
+
resolvedUrl: string;
|
|
8
11
|
}
|
|
9
12
|
declare const PivotLink: FC<PivotLinkProps>;
|
|
10
13
|
export default PivotLink;
|
|
@@ -3,6 +3,7 @@ import { ErrorOutline } from '@mui/icons-material';
|
|
|
3
3
|
import { Tooltip } from '@mui/material';
|
|
4
4
|
import { useHelpers } from '@cccsaurora/howler-ui/components/elements/display/handlebars/helpers';
|
|
5
5
|
import HowlerCard from '@cccsaurora/howler-ui/components/elements/display/HowlerCard';
|
|
6
|
+
import PivotTooltip from '@cccsaurora/howler-ui/components/elements/hit/PivotTooltip';
|
|
6
7
|
import Handlebars from 'handlebars';
|
|
7
8
|
import { isEmpty } from 'lodash-es';
|
|
8
9
|
import React, { useMemo } from 'react';
|
|
@@ -10,7 +11,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
10
11
|
import { usePluginStore } from 'react-pluggable';
|
|
11
12
|
import { flattenDeep } from '@cccsaurora/howler-ui/utils/utils';
|
|
12
13
|
import RelatedLink from './RelatedLink';
|
|
13
|
-
const PivotLink = ({ pivot, hit, compact = false }) => {
|
|
14
|
+
const PivotLink = ({ pivot, hit, compact = false, dossier, resolvedUrl }) => {
|
|
14
15
|
const { i18n } = useTranslation();
|
|
15
16
|
const helpers = useHelpers({ async: false, components: false });
|
|
16
17
|
const pluginStore = usePluginStore();
|
|
@@ -47,11 +48,13 @@ const PivotLink = ({ pivot, hit, compact = false }) => {
|
|
|
47
48
|
return handlebars.compile(pivot.value)(templateObject);
|
|
48
49
|
}
|
|
49
50
|
catch (e) {
|
|
51
|
+
// eslint-disable-next-line no-console
|
|
52
|
+
console.error(`Failed to compile pivot template for value "${pivot.value}":`, e);
|
|
50
53
|
return pivot.value;
|
|
51
54
|
}
|
|
52
55
|
}, [flatHit, pivot, handlebars, helpers]);
|
|
53
56
|
if (href) {
|
|
54
|
-
return (_jsx(RelatedLink, { title: pivot.label[i18n.language], href: href, compact: compact, icon: pivot.icon, target: "_blank", rel: "noopener noreferrer" }));
|
|
57
|
+
return (_jsx(RelatedLink, { title: pivot.label[i18n.language], href: href, compact: compact, icon: pivot.icon, target: "_blank", rel: "noopener noreferrer", tooltip: _jsx(PivotTooltip, { dossier: dossier, resolvedUrl: resolvedUrl }) }));
|
|
55
58
|
}
|
|
56
59
|
// Hide a relatively useless console error, we'll show a UI component instead
|
|
57
60
|
// eslint-disable-next-line no-console
|
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Stack, Typography } from '@mui/material';
|
|
2
|
+
import { Stack, Tooltip, Typography } from '@mui/material';
|
|
3
3
|
import HowlerCard from '@cccsaurora/howler-ui/components/elements/display/HowlerCard';
|
|
4
|
+
import RelatedLinkTooltip from '@cccsaurora/howler-ui/components/elements/hit/RelatedLinkTooltip';
|
|
4
5
|
import React, {} from 'react';
|
|
5
6
|
import { Link } from 'react-router-dom';
|
|
6
7
|
import RelatedIcon from './RelatedIcon';
|
|
7
|
-
const RelatedLink = ({ icon, title, href, target, rel, compact = false, children }) => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
8
|
+
const RelatedLink = ({ icon, title, href, target, rel, compact = false, tooltip, children }) => {
|
|
9
|
+
const safeTitle = title ?? href ?? '';
|
|
10
|
+
const tooltipContent = tooltip ?? _jsx(RelatedLinkTooltip, { title: safeTitle, href: href ?? '' });
|
|
11
|
+
return (_jsx(Tooltip, { title: tooltipContent, children: _jsx("div", { style: { display: 'flex' }, children: _jsx(HowlerCard, { variant: compact ? 'outlined' : 'elevation', onClick: () => href && window.open(href, target), sx: [
|
|
12
|
+
theme => ({
|
|
13
|
+
cursor: 'pointer',
|
|
14
|
+
backgroundColor: 'transparent',
|
|
15
|
+
transition: theme.transitions.create(['border-color']),
|
|
16
|
+
'&:hover': { borderColor: 'primary.main', '& a': { textDecoration: 'underline' } },
|
|
17
|
+
'& > div': {
|
|
18
|
+
height: '100%'
|
|
19
|
+
},
|
|
20
|
+
'& a': { textDecoration: 'none', color: 'text.primary' }
|
|
21
|
+
}),
|
|
22
|
+
!compact && { border: 'thin solid', borderColor: 'transparent' }
|
|
23
|
+
], children: _jsxs(Stack, { direction: "row", p: compact ? 0.5 : 1, spacing: 1, alignItems: "center", children: [children || _jsx(RelatedIcon, { icon: icon, title: title, href: href, compact: compact }), _jsx(Typography, { component: Link, to: href ?? '#', target: target, rel: rel, onClick: e => e.stopPropagation(), children: safeTitle })] }) }, href) }) }));
|
|
21
24
|
};
|
|
22
25
|
export default RelatedLink;
|
|
@@ -322,6 +322,8 @@
|
|
|
322
322
|
"modal.rule.title": "Create a New Rule",
|
|
323
323
|
"no.data": "No Data",
|
|
324
324
|
"none": "None",
|
|
325
|
+
"notebook.opens.panel": "Opens notebook panel",
|
|
326
|
+
"notebook.title": "Notebook",
|
|
325
327
|
"on": "on",
|
|
326
328
|
"open": "Open",
|
|
327
329
|
"operations.add_label": "Add Label",
|
|
@@ -427,6 +429,9 @@
|
|
|
427
429
|
"personalization.reset_text": "Reset to site defaults",
|
|
428
430
|
"personalization.showbreadcrumbs": "Show Breadcrumbs",
|
|
429
431
|
"personalization.sticky": "Sticky Topbar",
|
|
432
|
+
"pivot.description.none": "No description",
|
|
433
|
+
"pivot.dossier.open": "Open Dossier",
|
|
434
|
+
"pivot.url": "URL:",
|
|
430
435
|
"query": "Query",
|
|
431
436
|
"query.invalid": "Invalid query",
|
|
432
437
|
"quicksearch.aria": "search",
|
|
@@ -322,6 +322,8 @@
|
|
|
322
322
|
"modal.rule.title": "Créer une nouvelle règle",
|
|
323
323
|
"no.data": "Aucune donnée",
|
|
324
324
|
"none": "Rien",
|
|
325
|
+
"notebook.opens.panel": "Ouvre le panneau des notes",
|
|
326
|
+
"notebook.title": "Carnet",
|
|
325
327
|
"on": "sur",
|
|
326
328
|
"open": "Ouvert",
|
|
327
329
|
"operations.add_label": "Ajouter un label",
|
|
@@ -427,6 +429,9 @@
|
|
|
427
429
|
"personalization.reset_text": "Réinitialiser les paramètres",
|
|
428
430
|
"personalization.showbreadcrumbs": "Afficher fils d'ariane",
|
|
429
431
|
"personalization.sticky": "Barre supérieure collante",
|
|
432
|
+
"pivot.description.none": "Aucune description",
|
|
433
|
+
"pivot.dossier.open": "Ouvrir le dossier",
|
|
434
|
+
"pivot.url": "URL :",
|
|
430
435
|
"query": "Requête",
|
|
431
436
|
"query.invalid": "Requête invalide",
|
|
432
437
|
"quicksearch.aria": "recherche",
|