Simplify Drilldowns
This commit is contained in:
@@ -0,0 +1,161 @@
|
|||||||
|
import { css } from '@emotion/css';
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
|
import { Trans, t } from '@grafana/i18n';
|
||||||
|
import { config } from '@grafana/runtime';
|
||||||
|
import { useStyles2 } from '@grafana/ui';
|
||||||
|
import { useDispatch, useSelector } from 'app/types/store';
|
||||||
|
|
||||||
|
import { updatePanelIframeUrl } from '../../state/crdtSlice';
|
||||||
|
import { selectPanels } from '../../state/selectors';
|
||||||
|
|
||||||
|
interface ExploreMapDrilldownPanelProps {
|
||||||
|
exploreId: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
mode: 'traces-drilldown' | 'metrics-drilldown' | 'profiles-drilldown' | 'logs-drilldown';
|
||||||
|
appPath: string;
|
||||||
|
titleKey: string;
|
||||||
|
titleDefault: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ExploreMapDrilldownPanel({
|
||||||
|
exploreId,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
mode,
|
||||||
|
appPath,
|
||||||
|
titleKey,
|
||||||
|
titleDefault,
|
||||||
|
}: ExploreMapDrilldownPanelProps) {
|
||||||
|
const styles = useStyles2(getStyles);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [isInitialized, setIsInitialized] = useState(false);
|
||||||
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
||||||
|
|
||||||
|
// Find the panel with this exploreId to get saved state
|
||||||
|
const panel = useSelector((state) => {
|
||||||
|
const panels = selectPanels(state.exploreMapCRDT);
|
||||||
|
return Object.values(panels).find((p) => p.exploreId === exploreId);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build iframe URL - use saved URL if available, otherwise default
|
||||||
|
// IMPORTANT: Only compute this once on mount to prevent iframe reloads
|
||||||
|
const [drilldownUrl] = useState(() => {
|
||||||
|
// If we have a saved iframe URL from previous session, use it
|
||||||
|
if (panel?.mode === mode && panel?.iframeUrl) {
|
||||||
|
return panel.iframeUrl;
|
||||||
|
}
|
||||||
|
// Otherwise, construct the default URL
|
||||||
|
const origin = window.location.origin;
|
||||||
|
const subUrl = config.appSubUrl || '';
|
||||||
|
return `${origin}${subUrl}${appPath}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize immediately for drilldown panels
|
||||||
|
useEffect(() => {
|
||||||
|
setIsInitialized(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Auto-save iframe URL changes for drilldown panels
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const panelId = panel?.id;
|
||||||
|
if (!panelId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastSavedUrl = panel?.iframeUrl || '';
|
||||||
|
|
||||||
|
const checkAndSaveUrl = () => {
|
||||||
|
const iframe = iframeRef.current;
|
||||||
|
if (!iframe) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const currentUrl = iframe.contentWindow?.location.href;
|
||||||
|
if (currentUrl && currentUrl !== lastSavedUrl) {
|
||||||
|
lastSavedUrl = currentUrl;
|
||||||
|
dispatch(
|
||||||
|
updatePanelIframeUrl({
|
||||||
|
panelId,
|
||||||
|
iframeUrl: currentUrl,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else if (!currentUrl) {
|
||||||
|
console.log('No URL detected');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Cannot read iframe URL:', e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start checking periodically (every 2 seconds)
|
||||||
|
const intervalId = setInterval(checkAndSaveUrl, 2000);
|
||||||
|
|
||||||
|
// Also check immediately
|
||||||
|
setTimeout(checkAndSaveUrl, 100);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
};
|
||||||
|
// Intentionally not including panel.iframeUrl to avoid re-creating interval on every save
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [isInitialized, panel?.id, dispatch]);
|
||||||
|
|
||||||
|
if (!isInitialized) {
|
||||||
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
|
<div className={styles.loading}>
|
||||||
|
<Trans i18nKey="explore-map.panel.initializing">Initializing...</Trans>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={styles.container}
|
||||||
|
style={{
|
||||||
|
width: `${width}px`,
|
||||||
|
height: `${height}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<iframe
|
||||||
|
ref={iframeRef}
|
||||||
|
src={drilldownUrl}
|
||||||
|
title={t(titleKey, titleDefault)}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
border: 'none',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStyles = (theme: GrafanaTheme2) => {
|
||||||
|
return {
|
||||||
|
container: css({
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
overflow: 'hidden',
|
||||||
|
position: 'relative',
|
||||||
|
}),
|
||||||
|
loading: css({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
height: '100%',
|
||||||
|
color: theme.colors.text.secondary,
|
||||||
|
fontSize: theme.typography.body.fontSize,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
+10
-138
@@ -1,14 +1,4 @@
|
|||||||
import { css } from '@emotion/css';
|
import { ExploreMapDrilldownPanel } from './ExploreMapDrilldownPanel';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
|
||||||
import { Trans, t } from '@grafana/i18n';
|
|
||||||
import { config } from '@grafana/runtime';
|
|
||||||
import { useStyles2 } from '@grafana/ui';
|
|
||||||
import { useDispatch, useSelector } from 'app/types/store';
|
|
||||||
|
|
||||||
import { updatePanelIframeUrl } from '../../state/crdtSlice';
|
|
||||||
import { selectPanels } from '../../state/selectors';
|
|
||||||
|
|
||||||
interface ExploreMapLogsDrilldownPanelProps {
|
interface ExploreMapLogsDrilldownPanelProps {
|
||||||
exploreId: string;
|
exploreId: string;
|
||||||
@@ -17,134 +7,16 @@ interface ExploreMapLogsDrilldownPanelProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ExploreMapLogsDrilldownPanel({ exploreId, width, height }: ExploreMapLogsDrilldownPanelProps) {
|
export function ExploreMapLogsDrilldownPanel({ exploreId, width, height }: ExploreMapLogsDrilldownPanelProps) {
|
||||||
const styles = useStyles2(getStyles);
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const [isInitialized, setIsInitialized] = useState(false);
|
|
||||||
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
||||||
|
|
||||||
// Find the panel with this exploreId to get saved state
|
|
||||||
const panel = useSelector((state) => {
|
|
||||||
const panels = selectPanels(state.exploreMapCRDT);
|
|
||||||
return Object.values(panels).find((p) => p.exploreId === exploreId);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Build iframe URL for logs drilldown - use saved URL if available, otherwise default
|
|
||||||
// IMPORTANT: Only compute this once on mount to prevent iframe reloads
|
|
||||||
const [logsDrilldownUrl] = useState(() => {
|
|
||||||
// If we have a saved iframe URL from previous session, use it
|
|
||||||
if (panel?.mode === 'logs-drilldown' && panel?.iframeUrl) {
|
|
||||||
return panel.iframeUrl;
|
|
||||||
}
|
|
||||||
// Otherwise, construct the default URL
|
|
||||||
const origin = window.location.origin;
|
|
||||||
const subUrl = config.appSubUrl || '';
|
|
||||||
const appPath = '/a/grafana-lokiexplore-app';
|
|
||||||
return `${origin}${subUrl}${appPath}`;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialize immediately for logs drilldown panels
|
|
||||||
useEffect(() => {
|
|
||||||
setIsInitialized(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Auto-save iframe URL changes for logs-drilldown panels
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isInitialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const panelId = panel?.id;
|
|
||||||
if (!panelId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastSavedUrl = panel?.iframeUrl || '';
|
|
||||||
|
|
||||||
const checkAndSaveUrl = () => {
|
|
||||||
const iframe = iframeRef.current;
|
|
||||||
if (!iframe) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const currentUrl = iframe.contentWindow?.location.href;
|
|
||||||
if (currentUrl && currentUrl !== lastSavedUrl) {
|
|
||||||
lastSavedUrl = currentUrl;
|
|
||||||
dispatch(
|
|
||||||
updatePanelIframeUrl({
|
|
||||||
panelId,
|
|
||||||
iframeUrl: currentUrl,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else if (!currentUrl) {
|
|
||||||
console.log('No URL detected');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Cannot read iframe URL:', e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Start checking periodically (every 2 seconds)
|
|
||||||
const intervalId = setInterval(checkAndSaveUrl, 2000);
|
|
||||||
|
|
||||||
// Also check immediately
|
|
||||||
setTimeout(checkAndSaveUrl, 100);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
};
|
|
||||||
// Intentionally not including panel.iframeUrl to avoid re-creating interval on every save
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [isInitialized, panel?.id, dispatch]);
|
|
||||||
|
|
||||||
if (!isInitialized) {
|
|
||||||
return (
|
|
||||||
<div className={styles.container}>
|
|
||||||
<div className={styles.loading}>
|
|
||||||
<Trans i18nKey="explore-map.panel.initializing">Initializing...</Trans>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<ExploreMapDrilldownPanel
|
||||||
className={styles.container}
|
exploreId={exploreId}
|
||||||
style={{
|
width={width}
|
||||||
width: `${width}px`,
|
height={height}
|
||||||
height: `${height}px`,
|
mode="logs-drilldown"
|
||||||
}}
|
appPath="/a/grafana-lokiexplore-app"
|
||||||
>
|
titleKey="explore-map.panel.logs-drilldown"
|
||||||
<iframe
|
titleDefault="Logs Drilldown"
|
||||||
ref={iframeRef}
|
/>
|
||||||
src={logsDrilldownUrl}
|
|
||||||
title={t('explore-map.panel.logs-drilldown', 'Logs Drilldown')}
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
border: 'none',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => {
|
|
||||||
return {
|
|
||||||
container: css({
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
overflow: 'hidden',
|
|
||||||
position: 'relative',
|
|
||||||
}),
|
|
||||||
loading: css({
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
height: '100%',
|
|
||||||
color: theme.colors.text.secondary,
|
|
||||||
fontSize: theme.typography.body.fontSize,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|||||||
+10
-138
@@ -1,14 +1,4 @@
|
|||||||
import { css } from '@emotion/css';
|
import { ExploreMapDrilldownPanel } from './ExploreMapDrilldownPanel';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
|
||||||
import { Trans, t } from '@grafana/i18n';
|
|
||||||
import { config } from '@grafana/runtime';
|
|
||||||
import { useStyles2 } from '@grafana/ui';
|
|
||||||
import { useDispatch, useSelector } from 'app/types/store';
|
|
||||||
|
|
||||||
import { updatePanelIframeUrl } from '../../state/crdtSlice';
|
|
||||||
import { selectPanels } from '../../state/selectors';
|
|
||||||
|
|
||||||
interface ExploreMapMetricsDrilldownPanelProps {
|
interface ExploreMapMetricsDrilldownPanelProps {
|
||||||
exploreId: string;
|
exploreId: string;
|
||||||
@@ -17,134 +7,16 @@ interface ExploreMapMetricsDrilldownPanelProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ExploreMapMetricsDrilldownPanel({ exploreId, width, height }: ExploreMapMetricsDrilldownPanelProps) {
|
export function ExploreMapMetricsDrilldownPanel({ exploreId, width, height }: ExploreMapMetricsDrilldownPanelProps) {
|
||||||
const styles = useStyles2(getStyles);
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const [isInitialized, setIsInitialized] = useState(false);
|
|
||||||
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
||||||
|
|
||||||
// Find the panel with this exploreId to get saved state
|
|
||||||
const panel = useSelector((state) => {
|
|
||||||
const panels = selectPanels(state.exploreMapCRDT);
|
|
||||||
return Object.values(panels).find((p) => p.exploreId === exploreId);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Build iframe URL for metrics drilldown - use saved URL if available, otherwise default
|
|
||||||
// IMPORTANT: Only compute this once on mount to prevent iframe reloads
|
|
||||||
const [metricsDrilldownUrl] = useState(() => {
|
|
||||||
// If we have a saved iframe URL from previous session, use it
|
|
||||||
if (panel?.mode === 'metrics-drilldown' && panel?.iframeUrl) {
|
|
||||||
return panel.iframeUrl;
|
|
||||||
}
|
|
||||||
// Otherwise, construct the default URL
|
|
||||||
const origin = window.location.origin;
|
|
||||||
const subUrl = config.appSubUrl || '';
|
|
||||||
const appPath = '/a/grafana-metricsdrilldown-app';
|
|
||||||
return `${origin}${subUrl}${appPath}`;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialize immediately for metrics drilldown panels
|
|
||||||
useEffect(() => {
|
|
||||||
setIsInitialized(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Auto-save iframe URL changes for metrics-drilldown panels
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isInitialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const panelId = panel?.id;
|
|
||||||
if (!panelId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastSavedUrl = panel?.iframeUrl || '';
|
|
||||||
|
|
||||||
const checkAndSaveUrl = () => {
|
|
||||||
const iframe = iframeRef.current;
|
|
||||||
if (!iframe) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const currentUrl = iframe.contentWindow?.location.href;
|
|
||||||
if (currentUrl && currentUrl !== lastSavedUrl) {
|
|
||||||
lastSavedUrl = currentUrl;
|
|
||||||
dispatch(
|
|
||||||
updatePanelIframeUrl({
|
|
||||||
panelId,
|
|
||||||
iframeUrl: currentUrl,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else if (!currentUrl) {
|
|
||||||
console.log('No URL detected');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Cannot read iframe URL:', e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Start checking periodically (every 2 seconds)
|
|
||||||
const intervalId = setInterval(checkAndSaveUrl, 2000);
|
|
||||||
|
|
||||||
// Also check immediately
|
|
||||||
setTimeout(checkAndSaveUrl, 100);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
};
|
|
||||||
// Intentionally not including panel.iframeUrl to avoid re-creating interval on every save
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [isInitialized, panel?.id, dispatch]);
|
|
||||||
|
|
||||||
if (!isInitialized) {
|
|
||||||
return (
|
|
||||||
<div className={styles.container}>
|
|
||||||
<div className={styles.loading}>
|
|
||||||
<Trans i18nKey="explore-map.panel.initializing">Initializing...</Trans>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<ExploreMapDrilldownPanel
|
||||||
className={styles.container}
|
exploreId={exploreId}
|
||||||
style={{
|
width={width}
|
||||||
width: `${width}px`,
|
height={height}
|
||||||
height: `${height}px`,
|
mode="metrics-drilldown"
|
||||||
}}
|
appPath="/a/grafana-metricsdrilldown-app"
|
||||||
>
|
titleKey="explore-map.panel.metrics-drilldown"
|
||||||
<iframe
|
titleDefault="Metrics Drilldown"
|
||||||
ref={iframeRef}
|
/>
|
||||||
src={metricsDrilldownUrl}
|
|
||||||
title={t('explore-map.panel.metrics-drilldown', 'Metrics Drilldown')}
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
border: 'none',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => {
|
|
||||||
return {
|
|
||||||
container: css({
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
overflow: 'hidden',
|
|
||||||
position: 'relative',
|
|
||||||
}),
|
|
||||||
loading: css({
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
height: '100%',
|
|
||||||
color: theme.colors.text.secondary,
|
|
||||||
fontSize: theme.typography.body.fontSize,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|||||||
+10
-138
@@ -1,14 +1,4 @@
|
|||||||
import { css } from '@emotion/css';
|
import { ExploreMapDrilldownPanel } from './ExploreMapDrilldownPanel';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
|
||||||
import { Trans, t } from '@grafana/i18n';
|
|
||||||
import { config } from '@grafana/runtime';
|
|
||||||
import { useStyles2 } from '@grafana/ui';
|
|
||||||
import { useDispatch, useSelector } from 'app/types/store';
|
|
||||||
|
|
||||||
import { updatePanelIframeUrl } from '../../state/crdtSlice';
|
|
||||||
import { selectPanels } from '../../state/selectors';
|
|
||||||
|
|
||||||
interface ExploreMapProfilesDrilldownPanelProps {
|
interface ExploreMapProfilesDrilldownPanelProps {
|
||||||
exploreId: string;
|
exploreId: string;
|
||||||
@@ -17,134 +7,16 @@ interface ExploreMapProfilesDrilldownPanelProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ExploreMapProfilesDrilldownPanel({ exploreId, width, height }: ExploreMapProfilesDrilldownPanelProps) {
|
export function ExploreMapProfilesDrilldownPanel({ exploreId, width, height }: ExploreMapProfilesDrilldownPanelProps) {
|
||||||
const styles = useStyles2(getStyles);
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const [isInitialized, setIsInitialized] = useState(false);
|
|
||||||
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
||||||
|
|
||||||
// Find the panel with this exploreId to get saved state
|
|
||||||
const panel = useSelector((state) => {
|
|
||||||
const panels = selectPanels(state.exploreMapCRDT);
|
|
||||||
return Object.values(panels).find((p) => p.exploreId === exploreId);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Build iframe URL for profiles drilldown - use saved URL if available, otherwise default
|
|
||||||
// IMPORTANT: Only compute this once on mount to prevent iframe reloads
|
|
||||||
const [profilesDrilldownUrl] = useState(() => {
|
|
||||||
// If we have a saved iframe URL from previous session, use it
|
|
||||||
if (panel?.mode === 'profiles-drilldown' && panel?.iframeUrl) {
|
|
||||||
return panel.iframeUrl;
|
|
||||||
}
|
|
||||||
// Otherwise, construct the default URL
|
|
||||||
const origin = window.location.origin;
|
|
||||||
const subUrl = config.appSubUrl || '';
|
|
||||||
const appPath = '/a/grafana-pyroscope-app';
|
|
||||||
return `${origin}${subUrl}${appPath}`;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialize immediately for profiles drilldown panels
|
|
||||||
useEffect(() => {
|
|
||||||
setIsInitialized(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Auto-save iframe URL changes for profiles-drilldown panels
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isInitialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const panelId = panel?.id;
|
|
||||||
if (!panelId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastSavedUrl = panel?.iframeUrl || '';
|
|
||||||
|
|
||||||
const checkAndSaveUrl = () => {
|
|
||||||
const iframe = iframeRef.current;
|
|
||||||
if (!iframe) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const currentUrl = iframe.contentWindow?.location.href;
|
|
||||||
if (currentUrl && currentUrl !== lastSavedUrl) {
|
|
||||||
lastSavedUrl = currentUrl;
|
|
||||||
dispatch(
|
|
||||||
updatePanelIframeUrl({
|
|
||||||
panelId,
|
|
||||||
iframeUrl: currentUrl,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else if (!currentUrl) {
|
|
||||||
console.log('No URL detected');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Cannot read iframe URL:', e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Start checking periodically (every 2 seconds)
|
|
||||||
const intervalId = setInterval(checkAndSaveUrl, 2000);
|
|
||||||
|
|
||||||
// Also check immediately
|
|
||||||
setTimeout(checkAndSaveUrl, 100);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
};
|
|
||||||
// Intentionally not including panel.iframeUrl to avoid re-creating interval on every save
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [isInitialized, panel?.id, dispatch]);
|
|
||||||
|
|
||||||
if (!isInitialized) {
|
|
||||||
return (
|
|
||||||
<div className={styles.container}>
|
|
||||||
<div className={styles.loading}>
|
|
||||||
<Trans i18nKey="explore-map.panel.initializing">Initializing...</Trans>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<ExploreMapDrilldownPanel
|
||||||
className={styles.container}
|
exploreId={exploreId}
|
||||||
style={{
|
width={width}
|
||||||
width: `${width}px`,
|
height={height}
|
||||||
height: `${height}px`,
|
mode="profiles-drilldown"
|
||||||
}}
|
appPath="/a/grafana-pyroscope-app"
|
||||||
>
|
titleKey="explore-map.panel.profiles-drilldown"
|
||||||
<iframe
|
titleDefault="Profiles Drilldown"
|
||||||
ref={iframeRef}
|
/>
|
||||||
src={profilesDrilldownUrl}
|
|
||||||
title={t('explore-map.panel.profiles-drilldown', 'Profiles Drilldown')}
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
border: 'none',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => {
|
|
||||||
return {
|
|
||||||
container: css({
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
overflow: 'hidden',
|
|
||||||
position: 'relative',
|
|
||||||
}),
|
|
||||||
loading: css({
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
height: '100%',
|
|
||||||
color: theme.colors.text.secondary,
|
|
||||||
fontSize: theme.typography.body.fontSize,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|||||||
+10
-138
@@ -1,14 +1,4 @@
|
|||||||
import { css } from '@emotion/css';
|
import { ExploreMapDrilldownPanel } from './ExploreMapDrilldownPanel';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
|
||||||
import { Trans, t } from '@grafana/i18n';
|
|
||||||
import { config } from '@grafana/runtime';
|
|
||||||
import { useStyles2 } from '@grafana/ui';
|
|
||||||
import { useDispatch, useSelector } from 'app/types/store';
|
|
||||||
|
|
||||||
import { updatePanelIframeUrl } from '../../state/crdtSlice';
|
|
||||||
import { selectPanels } from '../../state/selectors';
|
|
||||||
|
|
||||||
interface ExploreMapTracesDrilldownPanelProps {
|
interface ExploreMapTracesDrilldownPanelProps {
|
||||||
exploreId: string;
|
exploreId: string;
|
||||||
@@ -17,134 +7,16 @@ interface ExploreMapTracesDrilldownPanelProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ExploreMapTracesDrilldownPanel({ exploreId, width, height }: ExploreMapTracesDrilldownPanelProps) {
|
export function ExploreMapTracesDrilldownPanel({ exploreId, width, height }: ExploreMapTracesDrilldownPanelProps) {
|
||||||
const styles = useStyles2(getStyles);
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const [isInitialized, setIsInitialized] = useState(false);
|
|
||||||
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
||||||
|
|
||||||
// Find the panel with this exploreId to get saved state
|
|
||||||
const panel = useSelector((state) => {
|
|
||||||
const panels = selectPanels(state.exploreMapCRDT);
|
|
||||||
return Object.values(panels).find((p) => p.exploreId === exploreId);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Build iframe URL for traces drilldown - use saved URL if available, otherwise default
|
|
||||||
// IMPORTANT: Only compute this once on mount to prevent iframe reloads
|
|
||||||
const [tracesDrilldownUrl] = useState(() => {
|
|
||||||
// If we have a saved iframe URL from previous session, use it
|
|
||||||
if (panel?.mode === 'traces-drilldown' && panel?.iframeUrl) {
|
|
||||||
return panel.iframeUrl;
|
|
||||||
}
|
|
||||||
// Otherwise, construct the default URL
|
|
||||||
const origin = window.location.origin;
|
|
||||||
const subUrl = config.appSubUrl || '';
|
|
||||||
const appPath = '/a/grafana-exploretraces-app';
|
|
||||||
return `${origin}${subUrl}${appPath}`;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialize immediately for traces drilldown panels
|
|
||||||
useEffect(() => {
|
|
||||||
setIsInitialized(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Auto-save iframe URL changes for traces-drilldown panels
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isInitialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const panelId = panel?.id;
|
|
||||||
if (!panelId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastSavedUrl = panel?.iframeUrl || '';
|
|
||||||
|
|
||||||
const checkAndSaveUrl = () => {
|
|
||||||
const iframe = iframeRef.current;
|
|
||||||
if (!iframe) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const currentUrl = iframe.contentWindow?.location.href;
|
|
||||||
if (currentUrl && currentUrl !== lastSavedUrl) {
|
|
||||||
lastSavedUrl = currentUrl;
|
|
||||||
dispatch(
|
|
||||||
updatePanelIframeUrl({
|
|
||||||
panelId,
|
|
||||||
iframeUrl: currentUrl,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else if (!currentUrl) {
|
|
||||||
console.log('No URL detected');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Cannot read iframe URL:', e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Start checking periodically (every 2 seconds)
|
|
||||||
const intervalId = setInterval(checkAndSaveUrl, 2000);
|
|
||||||
|
|
||||||
// Also check immediately
|
|
||||||
setTimeout(checkAndSaveUrl, 100);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
};
|
|
||||||
// Intentionally not including panel.iframeUrl to avoid re-creating interval on every save
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [isInitialized, panel?.id, dispatch]);
|
|
||||||
|
|
||||||
if (!isInitialized) {
|
|
||||||
return (
|
|
||||||
<div className={styles.container}>
|
|
||||||
<div className={styles.loading}>
|
|
||||||
<Trans i18nKey="explore-map.panel.initializing">Initializing...</Trans>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<ExploreMapDrilldownPanel
|
||||||
className={styles.container}
|
exploreId={exploreId}
|
||||||
style={{
|
width={width}
|
||||||
width: `${width}px`,
|
height={height}
|
||||||
height: `${height}px`,
|
mode="traces-drilldown"
|
||||||
}}
|
appPath="/a/grafana-exploretraces-app"
|
||||||
>
|
titleKey="explore-map.panel.traces-drilldown"
|
||||||
<iframe
|
titleDefault="Traces Drilldown"
|
||||||
ref={iframeRef}
|
/>
|
||||||
src={tracesDrilldownUrl}
|
|
||||||
title={t('explore-map.panel.traces-drilldown', 'Traces Drilldown')}
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
border: 'none',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => {
|
|
||||||
return {
|
|
||||||
container: css({
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
overflow: 'hidden',
|
|
||||||
position: 'relative',
|
|
||||||
}),
|
|
||||||
loading: css({
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
height: '100%',
|
|
||||||
color: theme.colors.text.secondary,
|
|
||||||
fontSize: theme.typography.body.fontSize,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|||||||
@@ -480,7 +480,7 @@ const crdtSlice = createSlice({
|
|||||||
width: sourcePanel.position.width,
|
width: sourcePanel.position.width,
|
||||||
height: sourcePanel.position.height,
|
height: sourcePanel.position.height,
|
||||||
},
|
},
|
||||||
(sourcePanel.mode || 'explore') as 'explore' | 'traces-drilldown' | 'metrics-drilldown'
|
(sourcePanel.mode || 'explore') as 'explore' | 'traces-drilldown' | 'metrics-drilldown' | 'profiles-drilldown' | 'logs-drilldown'
|
||||||
);
|
);
|
||||||
|
|
||||||
manager.applyOperation(addOperation);
|
manager.applyOperation(addOperation);
|
||||||
|
|||||||
Reference in New Issue
Block a user