mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Synthetics UI] Add Actions popover menu (#136992)
This commit is contained in:
parent
7be8ae67f9
commit
4c5043c779
10 changed files with 424 additions and 52 deletions
|
@ -13,6 +13,7 @@ import {
|
|||
ConfigKey,
|
||||
EncryptedSyntheticsSavedMonitor,
|
||||
} from '../../../../../../../common/runtime_types';
|
||||
import { useMonitorDetailLocator } from '../../hooks/use_monitor_detail_locator';
|
||||
|
||||
export const MonitorDetailsLink = ({
|
||||
basePath,
|
||||
|
@ -30,13 +31,11 @@ export const MonitorDetailsLink = ({
|
|||
const locationId =
|
||||
lastSelectedLocationId && monitorHasLocation ? lastSelectedLocationId : firstMonitorLocationId;
|
||||
|
||||
const locationUrlQueryParam = locationId ? `?locationId=${locationId}` : '';
|
||||
const monitorDetailLinkUrl = useMonitorDetailLocator({ monitorId: monitor.id, locationId });
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiLink href={`${basePath}/app/synthetics/monitor/${monitor.id}${locationUrlQueryParam}`}>
|
||||
{monitor.name}
|
||||
</EuiLink>
|
||||
<EuiLink href={monitorDetailLinkUrl}>{monitor.name}</EuiLink>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import { render } from '../../../../utils/testing/rtl_helpers';
|
||||
import { ActionsPopover } from './actions_popover';
|
||||
import * as editMonitorLocatorModule from '../../hooks/use_edit_monitor_locator';
|
||||
import * as monitorDetailLocatorModule from '../../hooks/use_monitor_detail_locator';
|
||||
import * as monitorEnableHandlerModule from '../../../../hooks/use_monitor_enable_handler';
|
||||
import { FETCH_STATUS } from '@kbn/observability-plugin/public';
|
||||
import { MonitorOverviewItem } from '../types';
|
||||
|
||||
describe('ActionsPopover', () => {
|
||||
let testMonitor: MonitorOverviewItem;
|
||||
|
||||
beforeEach(() => {
|
||||
testMonitor = {
|
||||
location: {
|
||||
id: 'us_central',
|
||||
isServiceManaged: true,
|
||||
},
|
||||
isEnabled: true,
|
||||
name: 'Monitor 1',
|
||||
id: 'somelongstring',
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('renders the popover button', () => {
|
||||
const { queryByText, getByLabelText } = render(
|
||||
<ActionsPopover isPopoverOpen={false} setIsPopoverOpen={jest.fn()} monitor={testMonitor} />
|
||||
);
|
||||
expect(getByLabelText('Open actions menu'));
|
||||
expect(queryByText('Actions')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('opens the popover on click', async () => {
|
||||
const setIsPopoverOpen = jest.fn();
|
||||
const isPopoverOpen = false;
|
||||
const { getByLabelText } = render(
|
||||
<ActionsPopover
|
||||
isPopoverOpen={isPopoverOpen}
|
||||
setIsPopoverOpen={setIsPopoverOpen}
|
||||
monitor={testMonitor}
|
||||
/>
|
||||
);
|
||||
const popoverButton = getByLabelText('Open actions menu');
|
||||
fireEvent.click(popoverButton);
|
||||
expect(setIsPopoverOpen).toHaveBeenCalled();
|
||||
// the popover passes back a function that accepts a bool and returns the inverse,
|
||||
// so we're calling it here just to make sure the behavior is correct
|
||||
expect(setIsPopoverOpen.mock.calls[0][0](isPopoverOpen)).toBe(true);
|
||||
});
|
||||
|
||||
it('closes the popover on subsequent click', async () => {
|
||||
const setIsPopoverOpen = jest.fn();
|
||||
const isPopoverOpen = true;
|
||||
const { getByLabelText } = render(
|
||||
<ActionsPopover
|
||||
isPopoverOpen={isPopoverOpen}
|
||||
setIsPopoverOpen={setIsPopoverOpen}
|
||||
monitor={testMonitor}
|
||||
/>
|
||||
);
|
||||
const popoverButton = getByLabelText('Open actions menu');
|
||||
fireEvent.click(popoverButton);
|
||||
expect(setIsPopoverOpen).toHaveBeenCalled();
|
||||
// the popover passes back a function that accepts a bool and returns the inverse,
|
||||
// so we're calling it here just to make sure the behavior is correct
|
||||
expect(setIsPopoverOpen.mock.calls[0][0](isPopoverOpen)).toBe(false);
|
||||
});
|
||||
|
||||
it('contains link to edit page', async () => {
|
||||
jest
|
||||
.spyOn(editMonitorLocatorModule, 'useEditMonitorLocator')
|
||||
.mockReturnValue('/a/test/edit/url');
|
||||
const { getByRole } = render(
|
||||
<ActionsPopover isPopoverOpen={true} setIsPopoverOpen={jest.fn()} monitor={testMonitor} />
|
||||
);
|
||||
expect(getByRole('link')?.getAttribute('href')).toBe('/a/test/edit/url');
|
||||
});
|
||||
|
||||
it('contains link to detail page', async () => {
|
||||
jest
|
||||
.spyOn(monitorDetailLocatorModule, 'useMonitorDetailLocator')
|
||||
.mockReturnValue('/a/test/detail/url');
|
||||
const { getByRole } = render(
|
||||
<ActionsPopover isPopoverOpen={true} setIsPopoverOpen={jest.fn()} monitor={testMonitor} />
|
||||
);
|
||||
expect(getByRole('link')?.getAttribute('href')).toBe('/a/test/detail/url');
|
||||
});
|
||||
|
||||
it('sets the enabled state', async () => {
|
||||
const updateMonitorEnabledState = jest.fn();
|
||||
jest.spyOn(monitorEnableHandlerModule, 'useMonitorEnableHandler').mockReturnValue({
|
||||
status: FETCH_STATUS.SUCCESS,
|
||||
isEnabled: true,
|
||||
updateMonitorEnabledState,
|
||||
});
|
||||
const { getByText } = render(
|
||||
<ActionsPopover isPopoverOpen={true} setIsPopoverOpen={jest.fn()} monitor={testMonitor} />
|
||||
);
|
||||
const enableButton = getByText('Disable monitor');
|
||||
fireEvent.click(enableButton);
|
||||
expect(updateMonitorEnabledState).toHaveBeenCalledTimes(1);
|
||||
expect(updateMonitorEnabledState.mock.calls[0]).toEqual([
|
||||
{
|
||||
id: 'somelongstring',
|
||||
isEnabled: true,
|
||||
location: { id: 'us_central', isServiceManaged: true },
|
||||
name: 'Monitor 1',
|
||||
},
|
||||
false,
|
||||
]);
|
||||
});
|
||||
|
||||
it('sets enabled state to true', async () => {
|
||||
const updateMonitorEnabledState = jest.fn();
|
||||
jest.spyOn(monitorEnableHandlerModule, 'useMonitorEnableHandler').mockReturnValue({
|
||||
status: FETCH_STATUS.PENDING,
|
||||
isEnabled: null,
|
||||
updateMonitorEnabledState,
|
||||
});
|
||||
const { getByText } = render(
|
||||
<ActionsPopover
|
||||
isPopoverOpen={true}
|
||||
setIsPopoverOpen={jest.fn()}
|
||||
monitor={{ ...testMonitor, isEnabled: false }}
|
||||
/>
|
||||
);
|
||||
const enableButton = getByText('Enable monitor');
|
||||
fireEvent.click(enableButton);
|
||||
expect(updateMonitorEnabledState).toHaveBeenCalledTimes(1);
|
||||
expect(updateMonitorEnabledState.mock.calls[0]).toEqual([
|
||||
{
|
||||
id: 'somelongstring',
|
||||
isEnabled: false,
|
||||
location: { id: 'us_central', isServiceManaged: true },
|
||||
name: 'Monitor 1',
|
||||
},
|
||||
true,
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
import { EuiPopover, EuiButtonIcon, EuiContextMenu, useEuiShadow } from '@elastic/eui';
|
||||
import { FETCH_STATUS } from '@kbn/observability-plugin/public';
|
||||
import { useTheme } from '@kbn/observability-plugin/public';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import styled from 'styled-components';
|
||||
import { MonitorOverviewItem } from '../../../../../../../common/runtime_types';
|
||||
import { useMonitorEnableHandler } from '../../../../hooks/use_monitor_enable_handler';
|
||||
import { quietFetchOverviewAction } from '../../../../state/overview/actions';
|
||||
import { selectOverviewState } from '../../../../state/overview/selectors';
|
||||
import { useEditMonitorLocator } from '../../hooks/use_edit_monitor_locator';
|
||||
import { useMonitorDetailLocator } from '../../hooks/use_monitor_detail_locator';
|
||||
|
||||
interface ActionContainerProps {
|
||||
boxShadow: string;
|
||||
}
|
||||
|
||||
const ActionContainer = styled.div<ActionContainerProps>`
|
||||
// position
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
bottom: 42px;
|
||||
left: 12px;
|
||||
z-index: 1;
|
||||
|
||||
// style
|
||||
border-radius: ${({ theme }) => theme.eui.euiBorderRadius};
|
||||
${({ boxShadow }) => boxShadow}
|
||||
`;
|
||||
|
||||
export function ActionsPopover({
|
||||
isPopoverOpen,
|
||||
setIsPopoverOpen,
|
||||
monitor,
|
||||
}: {
|
||||
isPopoverOpen: boolean;
|
||||
monitor: MonitorOverviewItem;
|
||||
setIsPopoverOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
const euiShadow = useEuiShadow('l');
|
||||
const dispatch = useDispatch();
|
||||
const { pageState } = useSelector(selectOverviewState);
|
||||
|
||||
const detailUrl = useMonitorDetailLocator({
|
||||
monitorId: monitor.id,
|
||||
locationId: monitor.location.id,
|
||||
});
|
||||
const editUrl = useEditMonitorLocator({ monitorId: monitor.id });
|
||||
|
||||
const labels = useMemo(
|
||||
() => ({
|
||||
enabledSuccessLabel: enabledSuccessLabel(monitor.name),
|
||||
disabledSuccessLabel: disabledSuccessLabel(monitor.name),
|
||||
failureLabel: enabledFailLabel(monitor.name),
|
||||
}),
|
||||
[monitor.name]
|
||||
);
|
||||
const { status, isEnabled, updateMonitorEnabledState } = useMonitorEnableHandler({
|
||||
id: monitor.id,
|
||||
reloadPage: useCallback(() => {
|
||||
dispatch(quietFetchOverviewAction.get(pageState));
|
||||
setIsPopoverOpen(false);
|
||||
}, [dispatch, pageState, setIsPopoverOpen]),
|
||||
labels,
|
||||
});
|
||||
|
||||
const [enableLabel, setEnableLabel] = useState(
|
||||
monitor.isEnabled ? disableMonitorLabel : enableMonitorLabel
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (status === FETCH_STATUS.LOADING) {
|
||||
setEnableLabel(enableLabelLoading);
|
||||
} else if (status === FETCH_STATUS.SUCCESS) {
|
||||
setEnableLabel(isEnabled ? disableMonitorLabel : enableMonitorLabel);
|
||||
}
|
||||
}, [setEnableLabel, status, isEnabled, monitor.isEnabled]);
|
||||
|
||||
return (
|
||||
<ActionContainer boxShadow={euiShadow}>
|
||||
<EuiPopover
|
||||
button={
|
||||
<EuiButtonIcon
|
||||
aria-label={openActionsMenuAria}
|
||||
iconType="boxesHorizontal"
|
||||
color="primary"
|
||||
size="s"
|
||||
display="base"
|
||||
style={{ backgroundColor: theme.eui.euiColorLightestShade }}
|
||||
onClick={() => setIsPopoverOpen((b: boolean) => !b)}
|
||||
/>
|
||||
}
|
||||
color="lightestShade"
|
||||
isOpen={isPopoverOpen}
|
||||
closePopover={() => setIsPopoverOpen(false)}
|
||||
anchorPosition="rightUp"
|
||||
panelPaddingSize="none"
|
||||
>
|
||||
<EuiContextMenu
|
||||
initialPanelId={0}
|
||||
panels={[
|
||||
{
|
||||
id: '0',
|
||||
title: actionsMenuTitle,
|
||||
items: [
|
||||
{
|
||||
name: actionsMenuGoToMonitorName,
|
||||
icon: 'sortRight',
|
||||
href: detailUrl,
|
||||
},
|
||||
// not rendering this for now because it requires the detail flyout
|
||||
// which is not merged yet. Also, this needs to be rendered conditionally,
|
||||
// the actions menu can be opened within the flyout so there is no point in showing this
|
||||
// if the user is already in the flyout.
|
||||
// {
|
||||
// name: 'Quick inspect',
|
||||
// icon: 'inspect',
|
||||
// },
|
||||
// not rendering this for now because the manual test flyout is
|
||||
// still in the design phase
|
||||
// {
|
||||
// name: 'Run test manually',
|
||||
// icon: 'beaker',
|
||||
// },
|
||||
{
|
||||
name: actionsMenuEditMonitorName,
|
||||
icon: 'pencil',
|
||||
href: editUrl,
|
||||
},
|
||||
{
|
||||
name: enableLabel,
|
||||
icon: 'invert',
|
||||
onClick: () => {
|
||||
if (status !== FETCH_STATUS.LOADING)
|
||||
updateMonitorEnabledState(monitor, !monitor.isEnabled);
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</EuiPopover>
|
||||
</ActionContainer>
|
||||
);
|
||||
}
|
||||
|
||||
const openActionsMenuAria = i18n.translate(
|
||||
'xpack.synthetics.overview.actions.openPopover.ariaLabel',
|
||||
{
|
||||
defaultMessage: 'Open actions menu',
|
||||
}
|
||||
);
|
||||
|
||||
const actionsMenuTitle = i18n.translate('xpack.synthetics.overview.actions.menu.title', {
|
||||
defaultMessage: 'Actions',
|
||||
description: 'This is the text in the heading of a menu containing a set of actions',
|
||||
});
|
||||
|
||||
const actionsMenuGoToMonitorName = i18n.translate(
|
||||
'xpack.synthetics.overview.actions.goToMonitor.name',
|
||||
{
|
||||
defaultMessage: 'Go to monitor',
|
||||
description:
|
||||
'This is the text for a menu item that will take the user to the monitor detail page',
|
||||
}
|
||||
);
|
||||
|
||||
const actionsMenuEditMonitorName = i18n.translate(
|
||||
'xpack.synthetics.overview.actions.editMonitor.name',
|
||||
{
|
||||
defaultMessage: 'Edit monitor',
|
||||
description:
|
||||
'This is the text for a menu item that will take the user to the monitor edit page',
|
||||
}
|
||||
);
|
||||
|
||||
const enableLabelLoading = i18n.translate('xpack.synthetics.overview.actions.enableLabel', {
|
||||
defaultMessage: 'Loading...',
|
||||
});
|
||||
|
||||
const enableMonitorLabel = i18n.translate(
|
||||
'xpack.synthetics.overview.actions.enableLabelEnableMonitor',
|
||||
{
|
||||
defaultMessage: 'Enable monitor',
|
||||
}
|
||||
);
|
||||
|
||||
const disableMonitorLabel = i18n.translate(
|
||||
'xpack.synthetics.overview.actions.enableLabelDisableMonitor',
|
||||
{
|
||||
defaultMessage: 'Disable monitor',
|
||||
}
|
||||
);
|
||||
|
||||
const enabledSuccessLabel = (name: string) =>
|
||||
i18n.translate('xpack.synthetics.overview.actions.enabledSuccessLabel', {
|
||||
defaultMessage: 'Monitor "{name}" enabled successfully',
|
||||
values: { name },
|
||||
});
|
||||
|
||||
export const disabledSuccessLabel = (name: string) =>
|
||||
i18n.translate('xpack.synthetics.overview.actions.disabledSuccessLabel', {
|
||||
defaultMessage: 'Monitor "{name}" disabled successfully.',
|
||||
values: { name },
|
||||
});
|
||||
|
||||
export const enabledFailLabel = (name: string) =>
|
||||
i18n.translate('xpack.synthetics.overview.actions.enabledFailLabel', {
|
||||
defaultMessage: 'Unable to update monitor "{name}".',
|
||||
values: { name },
|
||||
});
|
|
@ -4,15 +4,16 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useState } from 'react';
|
||||
import { Chart, Settings, Metric, MetricTrendShape } from '@elastic/charts';
|
||||
import { EuiPanel, EuiLoadingChart } from '@elastic/eui';
|
||||
import { DARK_THEME } from '@elastic/charts';
|
||||
import { useTheme } from '@kbn/observability-plugin/public';
|
||||
import { useLocationName, useStatusByLocation } from '../../../../hooks';
|
||||
import { formatDuration } from '../../../../utils/formatting';
|
||||
import { Ping } from '../../../../../../../common/runtime_types';
|
||||
import { MonitorOverviewItem, Ping } from '../../../../../../../common/runtime_types';
|
||||
import { ActionsPopover } from './actions_popover';
|
||||
|
||||
export const getColor = (theme: ReturnType<typeof useTheme>, isEnabled: boolean, ping?: Ping) => {
|
||||
if (!isEnabled) {
|
||||
|
@ -24,24 +25,20 @@ export const getColor = (theme: ReturnType<typeof useTheme>, isEnabled: boolean,
|
|||
};
|
||||
|
||||
export const MetricItem = ({
|
||||
monitorId,
|
||||
locationId,
|
||||
monitorName,
|
||||
isMonitorEnabled,
|
||||
monitor,
|
||||
averageDuration,
|
||||
data,
|
||||
loaded,
|
||||
}: {
|
||||
monitorId: string;
|
||||
locationId: string;
|
||||
monitorName: string;
|
||||
isMonitorEnabled: boolean;
|
||||
monitor: MonitorOverviewItem;
|
||||
data: Array<{ x: number; y: number }>;
|
||||
averageDuration: number;
|
||||
loaded: boolean;
|
||||
}) => {
|
||||
const locationName = useLocationName({ locationId });
|
||||
const { locations } = useStatusByLocation(monitorId);
|
||||
const [isMouseOver, setIsMouseOver] = useState(false);
|
||||
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
||||
const locationName = useLocationName({ locationId: monitor.location?.id });
|
||||
const { locations } = useStatusByLocation(monitor.id);
|
||||
const ping = locations.find((loc) => loc.observer?.geo?.name === locationName);
|
||||
const theme = useTheme();
|
||||
|
||||
|
@ -53,6 +50,16 @@ export const MetricItem = ({
|
|||
>
|
||||
{loaded ? (
|
||||
<EuiPanel
|
||||
onMouseOver={() => {
|
||||
if (!isMouseOver) {
|
||||
setIsMouseOver(true);
|
||||
}
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
if (isMouseOver) {
|
||||
setIsMouseOver(false);
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
padding: '0px',
|
||||
height: '100%',
|
||||
|
@ -62,11 +69,11 @@ export const MetricItem = ({
|
|||
<Chart>
|
||||
<Settings baseTheme={DARK_THEME} />
|
||||
<Metric
|
||||
id={`${monitorId}-${locationId}`}
|
||||
id={`${monitor.id}-${monitor.location?.id}`}
|
||||
data={[
|
||||
[
|
||||
{
|
||||
title: monitorName,
|
||||
title: monitor.name,
|
||||
subtitle: locationName,
|
||||
value: averageDuration,
|
||||
trendShape: MetricTrendShape.Area,
|
||||
|
@ -79,12 +86,19 @@ export const MetricItem = ({
|
|||
</span>
|
||||
),
|
||||
valueFormatter: (d: number) => formatDuration(d),
|
||||
color: getColor(theme, isMonitorEnabled, ping),
|
||||
color: getColor(theme, monitor.isEnabled, ping),
|
||||
},
|
||||
],
|
||||
]}
|
||||
/>
|
||||
</Chart>
|
||||
{(isMouseOver || isPopoverOpen) && (
|
||||
<ActionsPopover
|
||||
monitor={monitor}
|
||||
isPopoverOpen={isPopoverOpen}
|
||||
setIsPopoverOpen={setIsPopoverOpen}
|
||||
/>
|
||||
)}
|
||||
</EuiPanel>
|
||||
) : (
|
||||
<EuiLoadingChart mono />
|
||||
|
|
|
@ -40,12 +40,7 @@ export const OverviewGrid = () => {
|
|||
key={`${monitor.id}-${monitor.location?.id}`}
|
||||
data-test-subj="syntheticsOverviewGridItem"
|
||||
>
|
||||
<OverviewGridItem
|
||||
monitorId={monitor.id}
|
||||
locationId={monitor.location?.id}
|
||||
monitorName={monitor.name}
|
||||
isMonitorEnabled={monitor.isEnabled}
|
||||
/>
|
||||
<OverviewGridItem monitor={monitor} />
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGrid>
|
||||
|
|
|
@ -7,28 +7,14 @@
|
|||
import React from 'react';
|
||||
import { MetricItem } from './metric_item';
|
||||
import { useLast50DurationChart } from '../../../../hooks';
|
||||
import { MonitorOverviewItem } from '../../../../../../../common/runtime_types';
|
||||
|
||||
export const OverviewGridItem = ({
|
||||
monitorId,
|
||||
monitorName,
|
||||
locationId,
|
||||
isMonitorEnabled,
|
||||
}: {
|
||||
monitorId: string;
|
||||
monitorName: string;
|
||||
locationId: string;
|
||||
isMonitorEnabled: boolean;
|
||||
}) => {
|
||||
const { data, loading, averageDuration } = useLast50DurationChart({ locationId, monitorId });
|
||||
export const OverviewGridItem = ({ monitor }: { monitor: MonitorOverviewItem }) => {
|
||||
const { data, loading, averageDuration } = useLast50DurationChart({
|
||||
locationId: monitor.location?.id,
|
||||
monitorId: monitor.id,
|
||||
});
|
||||
return (
|
||||
<MetricItem
|
||||
monitorId={monitorId}
|
||||
monitorName={monitorName}
|
||||
isMonitorEnabled={isMonitorEnabled}
|
||||
locationId={locationId}
|
||||
data={data}
|
||||
loaded={!loading}
|
||||
averageDuration={averageDuration}
|
||||
/>
|
||||
<MetricItem monitor={monitor} data={data} loaded={!loading} averageDuration={averageDuration} />
|
||||
);
|
||||
};
|
||||
|
|
|
@ -37,7 +37,7 @@ export function useLast50DurationChart({
|
|||
const coords = hits
|
||||
.reverse() // results are returned in desc order by timestamp. Reverse to ensure the data is in asc order by timestamp
|
||||
.map((hit, index) => {
|
||||
const duration = hit['monitor.duration.us']?.[0];
|
||||
const duration = hit?.['monitor.duration.us']?.[0];
|
||||
totalDuration += duration || 0;
|
||||
if (duration === undefined) {
|
||||
return null;
|
||||
|
|
|
@ -9,7 +9,11 @@ import { useKibana } from '@kbn/kibana-react-plugin/public';
|
|||
import { FETCH_STATUS } from '@kbn/observability-plugin/public';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { ConfigKey, EncryptedSyntheticsMonitor } from '../components/monitors_page/overview/types';
|
||||
import {
|
||||
ConfigKey,
|
||||
EncryptedSyntheticsMonitor,
|
||||
MonitorOverviewItem,
|
||||
} from '../components/monitors_page/overview/types';
|
||||
import {
|
||||
clearMonitorUpsertStatus,
|
||||
fetchUpsertMonitorAction,
|
||||
|
@ -37,7 +41,7 @@ export function useMonitorEnableHandler({
|
|||
const savedObjEnabledState = upsertStatuses[id]?.enabled;
|
||||
const [isEnabled, setIsEnabled] = useState<boolean | null>(null);
|
||||
const updateMonitorEnabledState = useCallback(
|
||||
(monitor: EncryptedSyntheticsMonitor, enabled: boolean) => {
|
||||
(monitor: EncryptedSyntheticsMonitor | MonitorOverviewItem, enabled: boolean) => {
|
||||
dispatch(
|
||||
fetchUpsertMonitorAction({
|
||||
id,
|
||||
|
@ -82,5 +86,5 @@ export function useMonitorEnableHandler({
|
|||
savedObjEnabledState,
|
||||
]);
|
||||
|
||||
return { isEnabled, setIsEnabled, updateMonitorEnabledState, status };
|
||||
return { isEnabled, updateMonitorEnabledState, status };
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import { createAction } from '@reduxjs/toolkit';
|
|||
import {
|
||||
EncryptedSyntheticsMonitor,
|
||||
MonitorManagementListResult,
|
||||
MonitorOverviewItem,
|
||||
} from '../../../../../common/runtime_types';
|
||||
import { createAsyncAction } from '../utils/actions';
|
||||
|
||||
|
@ -22,7 +23,7 @@ export const fetchMonitorListAction = createAsyncAction<
|
|||
|
||||
export interface UpsertMonitorRequest {
|
||||
id: string;
|
||||
monitor: EncryptedSyntheticsMonitor;
|
||||
monitor: EncryptedSyntheticsMonitor | MonitorOverviewItem;
|
||||
}
|
||||
export const fetchUpsertMonitorAction = createAction<UpsertMonitorRequest>('fetchUpsertMonitor');
|
||||
export const fetchUpsertSuccessAction = createAction<{
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
FetchMonitorManagementListQueryArgs,
|
||||
MonitorManagementListResult,
|
||||
MonitorManagementListResultCodec,
|
||||
MonitorOverviewItem,
|
||||
ServiceLocationErrors,
|
||||
SyntheticsMonitor,
|
||||
} from '../../../../../common/runtime_types';
|
||||
|
@ -54,7 +55,7 @@ export const fetchUpsertMonitor = async ({
|
|||
monitor,
|
||||
id,
|
||||
}: {
|
||||
monitor: SyntheticsMonitor | EncryptedSyntheticsMonitor;
|
||||
monitor: SyntheticsMonitor | EncryptedSyntheticsMonitor | MonitorOverviewItem;
|
||||
id?: string;
|
||||
}): Promise<{ attributes: { errors: ServiceLocationErrors } } | SyntheticsMonitor> => {
|
||||
if (id) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue