Skip to content

Commit

Permalink
[Synthetics] Fix overview error popover !! (#211431)
Browse files Browse the repository at this point in the history
## Summary

Fix overview error popover !!

Pings aren't being returned as part of overview data anymore, so had to
add redux actions to fetch it separately via an existing API

Fixes #211745


<img width="1728" alt="image"
src="https://github.com/user-attachments/assets/2244948f-e42d-443d-b6e7-42e0a72b1bfa"
/>

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Justin Kambic <jk@elastic.co>
  • Loading branch information
3 people authored Feb 21, 2025
1 parent 74ef9fc commit aaf73ff
Show file tree
Hide file tree
Showing 17 changed files with 235 additions and 100 deletions.
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,25 +13,25 @@ import { useKibana } from '@kbn/kibana-react-plugin/public';
import moment from 'moment';
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { OverviewStatusMetaData } from '../../../../../../../common/runtime_types';
import { ClientPluginsStart } from '../../../../../../plugin';
import { useLocationName, useStatusByLocationOverview } from '../../../../hooks';
import { OverviewStatusMetaData } from '../../../../../../../../common/runtime_types';
import { ClientPluginsStart } from '../../../../../../../plugin';
import { useLocationName, useStatusByLocationOverview } from '../../../../../hooks';
import {
selectErrorPopoverState,
selectOverviewTrends,
toggleErrorPopoverOpen,
} from '../../../../state';
} from '../../../../../state';
import {
hideTestNowFlyoutAction,
manualTestRunInProgressSelector,
toggleTestNowFlyoutAction,
} from '../../../../state/manual_test_runs';
import { formatDuration } from '../../../../utils/formatting';
import { ActionsPopover } from './actions_popover';
import { MetricItemBody } from './metric_item/metric_item_body';
import { MetricItemExtra } from './metric_item/metric_item_extra';
} 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 { FlyoutParamProps } from './types';
import { FlyoutParamProps } from '../types';

const METRIC_ITEM_HEIGHT = 160;

Expand Down Expand Up @@ -75,7 +75,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 @@ -190,7 +190,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

0 comments on commit aaf73ff

Please sign in to comment.