Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.x] [Synthetics] Fix overview error popover !! (#211431) #213328

Merged
merged 3 commits into from
Mar 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/kbn-babel-preset/styled_components_files.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ export const OverviewStatusMetaDataCodec = t.intersection([
t.partial({
projectId: t.string,
updated_at: t.string,
ping: OverviewPingCodec,
timestamp: t.string,
spaceId: t.string,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { OverviewLoader } from '../overview_loader';
import { useFilteredGroupMonitors } from './use_filtered_group_monitors';
import { OverviewStatusMetaData } from '../../types';
import { selectOverviewStatus } from '../../../../../state/overview_status';
import { MetricItem } from '../metric_item';
import { MetricItem } from '../metric_item/metric_item';

const PER_ROW = 4;
const DEFAULT_ROW_SIZE = 2;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { EuiButtonIcon, useEuiShadow, useEuiTheme } from '@elastic/eui';
import * as React from 'react';
import { useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { i18n } from '@kbn/i18n';
import { css } from '@emotion/react';
import { useSyntheticsSettingsContext } from '../../../../../contexts';
import { selectErrorPopoverState, toggleErrorPopoverOpen } from '../../../../../state';

export const MetricErrorIcon = ({ configIdByLocation }: { configIdByLocation: string }) => {
const isPopoverOpen = useSelector(selectErrorPopoverState);
const dispatch = useDispatch();

const setIsPopoverOpen = () => {
dispatch(toggleErrorPopoverOpen(configIdByLocation));
};
const timer = useRef<NodeJS.Timeout | null>(null);
const euiShadow = useEuiShadow('s');

const theme = useEuiTheme().euiTheme;
const { darkMode } = useSyntheticsSettingsContext();

return (
<div
css={css`
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
width: 32px;
height: 32px;
background: ${darkMode ? theme.colors.backgroundBaseSubdued : theme.colors.lightestShade};
border: 1px solid ${darkMode ? theme.colors.darkShade : theme.colors.lightShade};
box-shadow: ${euiShadow};
border-radius: 16px;
flex: none;
order: 0;
flex-grow: 0;
`}
onMouseEnter={() => {
// show popover with delay
if (timer.current) {
clearTimeout(timer.current);
}
timer.current = setTimeout(() => {
setIsPopoverOpen();
}, 300);
}}
onMouseLeave={() => {
if (isPopoverOpen) {
return;
} else if (timer.current) {
clearTimeout(timer.current);
}
}}
onClick={() => {
if (configIdByLocation === isPopoverOpen) {
dispatch(toggleErrorPopoverOpen(null));
} else {
dispatch(toggleErrorPopoverOpen(configIdByLocation));
}
}}
onKeyDown={(e) => {
if (e.key === 'Enter') {
if (configIdByLocation === isPopoverOpen) {
dispatch(toggleErrorPopoverOpen(null));
} else {
dispatch(toggleErrorPopoverOpen(configIdByLocation));
}
}
}}
>
<EuiButtonIcon
data-test-subj="syntheticsMetricItemIconButton"
iconType="warning"
color="danger"
size="m"
aria-label={ERROR_DETAILS}
/>
</div>
);
};
const ERROR_DETAILS = i18n.translate('xpack.synthetics.errorDetails.label', {
defaultMessage: 'Error details',
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,25 @@ import { EuiPanel, EuiSpacer } from '@elastic/eui';
import { useElasticChartsTheme } from '@kbn/charts-theme';
import { useTheme } from '@kbn/observability-shared-plugin/public';
import moment from 'moment';
import { useSelector, useDispatch } from 'react-redux';

import { FlyoutParamProps } from './types';
import { MetricItemBody } from './metric_item/metric_item_body';
import { useDispatch, useSelector } from 'react-redux';
import { OverviewStatusMetaData } from '../../../../../../../../common/runtime_types';
import { useLocationName, useStatusByLocationOverview } from '../../../../../hooks';
import {
selectErrorPopoverState,
selectOverviewTrends,
toggleErrorPopoverOpen,
} from '../../../../state';
import { useLocationName, useStatusByLocationOverview } from '../../../../hooks';
import { formatDuration } from '../../../../utils/formatting';
import { OverviewStatusMetaData } from '../../../../../../../common/runtime_types';
import { ActionsPopover } from './actions_popover';
} from '../../../../../state';
import {
hideTestNowFlyoutAction,
manualTestRunInProgressSelector,
toggleTestNowFlyoutAction,
} from '../../../../state/manual_test_runs';
} from '../../../../../state/manual_test_runs';
import { formatDuration } from '../../../../../utils/formatting';
import { ActionsPopover } from '../actions_popover';
import { MetricItemBody } from './metric_item_body';
import { MetricItemExtra } from './metric_item_extra';
import { MetricItemIcon } from './metric_item_icon';
import { MetricItemExtra } from './metric_item/metric_item_extra';
import { FlyoutParamProps } from '../types';

const METRIC_ITEM_HEIGHT = 160;

Expand Down Expand Up @@ -69,7 +68,7 @@ export const MetricItem = ({
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const isErrorPopoverOpen = useSelector(selectErrorPopoverState);
const locationName = useLocationName(monitor);
const { status, timestamp, ping, configIdByLocation } = useStatusByLocationOverview({
const { status, timestamp, configIdByLocation } = useStatusByLocationOverview({
configId: monitor.configId,
locationId: monitor.locationId,
});
Expand Down Expand Up @@ -184,7 +183,6 @@ export const MetricItem = ({
<MetricItemIcon
monitor={monitor}
status={status}
ping={ping}
timestamp={timestamp}
configIdByLocation={configIdByLocation}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,24 @@ import {
EuiPopoverTitle,
EuiPopoverFooter,
EuiButton,
useEuiShadow,
EuiCallOut,
EuiFlexGroup,
EuiFlexItem,
EuiLink,
EuiSpacer,
EuiSkeletonText,
} from '@elastic/eui';
import { useDispatch, useSelector } from 'react-redux';
import styled from 'styled-components';
import styled from '@emotion/styled';
import { i18n } from '@kbn/i18n';
import { euiStyled } from '@kbn/kibana-react-plugin/common';
import { useRef } from 'react';
import { selectErrorPopoverState, toggleErrorPopoverOpen } from '../../../../state';
import { useErrorDetailsLink } from '../../../common/links/error_details_link';
import { OverviewPing, OverviewStatusMetaData } from '../../../../../../../common/runtime_types';
import { isTestRunning, manualTestRunSelector } from '../../../../state/manual_test_runs';
import { useDateFormat } from '../../../../../../hooks/use_date_format';

import { MetricErrorIcon } from './metric_error_icon';
import { OverviewStatusMetaData } from '../../../../../../../../common/runtime_types';
import { isTestRunning, manualTestRunSelector } from '../../../../../state/manual_test_runs';
import { selectErrorPopoverState, toggleErrorPopoverOpen } from '../../../../../state';
import { useErrorDetailsLink } from '../../../../common/links/error_details_link';
import { useDateFormat } from '../../../../../../../hooks/use_date_format';
import { useLatestError } from './use_latest_error';

const Container = styled.div`
display: inline-block;
Expand All @@ -43,35 +44,31 @@ const Container = styled.div`
export const MetricItemIcon = ({
monitor,
status,
ping,
timestamp,
configIdByLocation,
}: {
monitor: OverviewStatusMetaData;
status: string;
configIdByLocation: string;
timestamp?: string;
ping?: OverviewPing;
}) => {
const testNowRun = useSelector(manualTestRunSelector(monitor.configId));
const isPopoverOpen = useSelector(selectErrorPopoverState);
const { latestPing } = useLatestError({
configIdByLocation,
monitorId: monitor.configId,
locationLabel: monitor.locationLabel,
});

const dispatch = useDispatch();

const timer = useRef<NodeJS.Timeout | null>(null);

const setIsPopoverOpen = () => {
dispatch(toggleErrorPopoverOpen(configIdByLocation));
};

const inProgress = isTestRunning(testNowRun);

const errorLink = useErrorDetailsLink({
configId: monitor.configId,
stateId: ping?.state?.id!,
stateId: latestPing?.state?.id!,
locationId: monitor.locationId,
});
const euiShadow = useEuiShadow('s');

const formatter = useDateFormat();
const testTime = formatter(timestamp);
Expand All @@ -94,42 +91,7 @@ export const MetricItemIcon = ({
return (
<Container>
<EuiPopover
button={
<StyledIcon
onMouseEnter={() => {
// show popover with delay
if (timer.current) {
clearTimeout(timer.current);
}
timer.current = setTimeout(() => {
setIsPopoverOpen();
}, 300);
}}
onMouseLeave={() => {
if (isPopoverOpen) {
return;
} else if (timer.current) {
clearTimeout(timer.current);
}
}}
boxShadow={euiShadow}
onClick={() => {
if (configIdByLocation === isPopoverOpen) {
dispatch(toggleErrorPopoverOpen(null));
} else {
dispatch(toggleErrorPopoverOpen(configIdByLocation));
}
}}
>
<EuiButtonIcon
data-test-subj="syntheticsMetricItemIconButton"
iconType="warning"
color="danger"
size="m"
aria-label={ERROR_DETAILS}
/>
</StyledIcon>
}
button={<MetricErrorIcon configIdByLocation={configIdByLocation} />}
isOpen={configIdByLocation === isPopoverOpen}
closePopover={closePopover}
anchorPosition="upCenter"
Expand All @@ -145,27 +107,43 @@ export const MetricItemIcon = ({
data-test-subj="syntheticsMetricItemIconButton"
iconType="cross"
onClick={closePopover}
aria-label={i18n.translate(
'xpack.synthetics.metricItemIcon.euiButtonIcon.closePopover',
{
defaultMessage: 'Close popover',
}
)}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPopoverTitle>
<div style={{ width: '300px' }}>
{ping?.url?.full && (
<div style={{ width: '300px', overflowWrap: 'break-word' }}>
{latestPing?.url?.full && (
<>
{i18n.translate('xpack.synthetics.metricItemIcon.div.urlLabel', {
defaultMessage: 'URL: ',
})}
<EuiLink
data-test-subj="syntheticsMetricItemIconLink"
href={ping.url.full}
href={latestPing.url.full}
target="_blank"
>
{ping.url.full}
{latestPing.url.full}
</EuiLink>
<EuiSpacer size="s" />
</>
)}
<EuiCallOut title={ping?.error?.message} color="danger" iconType="warning" />
<EuiCallOut
title={
latestPing?.error?.message ? (
latestPing?.error?.message
) : (
<EuiSkeletonText lines={2} />
)
}
color="danger"
iconType="warning"
/>
</div>
<EuiPopoverFooter>
<EuiButton
Expand All @@ -181,14 +159,14 @@ export const MetricItemIcon = ({
</Container>
);
} else {
if (ping?.url) {
if (latestPing?.url) {
return (
<Container>
<EuiButtonIcon
title={ping.url.full}
title={latestPing.url.full}
color="text"
data-test-subj="syntheticsMetricItemIconButton"
href={ping.url.full}
href={latestPing.url.full}
iconType="link"
target="_blank"
aria-label={i18n.translate('xpack.synthetics.metricItemIcon.euiButtonIcon.monitorUrl', {
Expand All @@ -209,22 +187,3 @@ const ERROR_DETAILS = i18n.translate('xpack.synthetics.errorDetails.label', {
const TEST_IN_PROGRESS = i18n.translate('xpack.synthetics.inProgress.label', {
defaultMessage: 'Manual test run is in progress.',
});

const StyledIcon = euiStyled.div<{ boxShadow: string }>`
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
width: 32px;
height: 32px;
background: ${({ theme }) =>
theme.darkMode ? theme.eui.euiColorDarkestShade : theme.eui.euiColorLightestShade};
border: 1px solid ${({ theme }) =>
theme.darkMode ? theme.eui.euiColorDarkShade : theme.eui.euiColorLightShade};
${({ boxShadow }) => boxShadow}
border-radius: 16px;
flex: none;
order: 0;
flex-grow: 0;
`;
Loading