mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Uptime][Monitor Management UI] Add Enabled column in monitor management monitors list table. (#121682) (elastic/uptime/issues/415)
* Add enabled column in monitor management monitors list table.
This commit is contained in:
parent
25f4d0cc0b
commit
786f41631a
6 changed files with 215 additions and 13 deletions
|
@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { EuiButtonIcon, EuiFlexItem, EuiFlexGroup, EuiLoadingSpinner } from '@elastic/eui';
|
||||
import { UptimeSettingsContext } from '../../../contexts';
|
||||
import { useFetcher, FETCH_STATUS } from '../../../../../observability/public';
|
||||
import { deleteMonitor } from '../../../state/api/monitor_management';
|
||||
import { deleteMonitor } from '../../../state/api';
|
||||
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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 { screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { ConfigKey, DataStream, SyntheticsMonitor } from '../../../../common/runtime_types';
|
||||
import { render } from '../../../lib/helper/rtl_helpers';
|
||||
import { FETCH_STATUS } from '../../../../../observability/public';
|
||||
import { spyOnUseFetcher } from '../../../lib/helper/spy_use_fetcher';
|
||||
import { MonitorEnabled } from './monitor_enabled';
|
||||
|
||||
describe('<MonitorEnabled />', () => {
|
||||
const setRefresh = jest.fn();
|
||||
const testMonitor = {
|
||||
[ConfigKey.MONITOR_TYPE]: DataStream.HTTP,
|
||||
[ConfigKey.ENABLED]: true,
|
||||
} as unknown as SyntheticsMonitor;
|
||||
|
||||
const assertMonitorEnabled = (button: HTMLButtonElement) =>
|
||||
expect(button).toHaveAttribute('aria-checked', 'true');
|
||||
const assertMonitorDisabled = (button: HTMLButtonElement) =>
|
||||
expect(button).toHaveAttribute('aria-checked', 'false');
|
||||
|
||||
let useFetcher: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
useFetcher?.mockClear();
|
||||
useFetcher = spyOnUseFetcher({});
|
||||
});
|
||||
|
||||
it('correctly renders "enabled" state', () => {
|
||||
render(<MonitorEnabled id="test-id" monitor={testMonitor} setRefresh={setRefresh} />);
|
||||
|
||||
const switchButton = screen.getByRole('switch') as HTMLButtonElement;
|
||||
assertMonitorEnabled(switchButton);
|
||||
});
|
||||
|
||||
it('correctly renders "disabled" state', () => {
|
||||
render(
|
||||
<MonitorEnabled
|
||||
id="test-id"
|
||||
monitor={{ ...testMonitor, [ConfigKey.ENABLED]: false }}
|
||||
setRefresh={setRefresh}
|
||||
/>
|
||||
);
|
||||
|
||||
const switchButton = screen.getByRole('switch') as HTMLButtonElement;
|
||||
assertMonitorDisabled(switchButton);
|
||||
});
|
||||
|
||||
it('toggles on click', () => {
|
||||
render(<MonitorEnabled id="test-id" monitor={testMonitor} setRefresh={setRefresh} />);
|
||||
|
||||
const switchButton = screen.getByRole('switch') as HTMLButtonElement;
|
||||
userEvent.click(switchButton);
|
||||
assertMonitorDisabled(switchButton);
|
||||
userEvent.click(switchButton);
|
||||
assertMonitorEnabled(switchButton);
|
||||
});
|
||||
|
||||
it('is disabled while request is in progress', () => {
|
||||
useFetcher.mockReturnValue({
|
||||
data: {},
|
||||
status: FETCH_STATUS.LOADING,
|
||||
refetch: () => {},
|
||||
});
|
||||
|
||||
render(<MonitorEnabled id="test-id" monitor={testMonitor} setRefresh={setRefresh} />);
|
||||
const switchButton = screen.getByRole('switch') as HTMLButtonElement;
|
||||
userEvent.click(switchButton);
|
||||
|
||||
expect(switchButton).toHaveAttribute('disabled');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* 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 { EuiSwitch, EuiProgress, EuiSwitchEvent } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
import { FETCH_STATUS, useFetcher } from '../../../../../observability/public';
|
||||
import { ConfigKey, SyntheticsMonitor } from '../../../../common/runtime_types';
|
||||
import { setMonitor } from '../../../state/api';
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
monitor: SyntheticsMonitor;
|
||||
setRefresh: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
export const MonitorEnabled = ({ id, monitor, setRefresh }: Props) => {
|
||||
const [isEnabled, setIsEnabled] = useState<boolean | null>(null);
|
||||
|
||||
const { notifications } = useKibana();
|
||||
|
||||
const { status } = useFetcher(() => {
|
||||
if (isEnabled !== null) {
|
||||
return setMonitor({ id, monitor: { ...monitor, [ConfigKey.ENABLED]: isEnabled } });
|
||||
}
|
||||
}, [isEnabled]);
|
||||
|
||||
useEffect(() => {
|
||||
if (status === FETCH_STATUS.FAILURE) {
|
||||
notifications.toasts.danger({
|
||||
title: (
|
||||
<p data-test-subj="uptimeMonitorEnabledUpdateFailure">
|
||||
{getMonitorEnabledUpdateFailureMessage(monitor[ConfigKey.NAME])}
|
||||
</p>
|
||||
),
|
||||
toastLifeTimeMs: 3000,
|
||||
});
|
||||
setIsEnabled(null);
|
||||
} else if (status === FETCH_STATUS.SUCCESS) {
|
||||
notifications.toasts.success({
|
||||
title: (
|
||||
<p data-test-subj="uptimeMonitorEnabledUpdateSuccess">
|
||||
{isEnabled
|
||||
? getMonitorEnabledSuccessLabel(monitor[ConfigKey.NAME])
|
||||
: getMonitorDisabledSuccessLabel(monitor[ConfigKey.NAME])}
|
||||
</p>
|
||||
),
|
||||
toastLifeTimeMs: 3000,
|
||||
});
|
||||
setRefresh(true);
|
||||
}
|
||||
}, [status]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const enabled = isEnabled ?? monitor[ConfigKey.ENABLED];
|
||||
const isLoading = status === FETCH_STATUS.LOADING;
|
||||
|
||||
const handleEnabledChange = (event: EuiSwitchEvent) => {
|
||||
const checked = event.target.checked;
|
||||
setIsEnabled(checked);
|
||||
};
|
||||
|
||||
return (
|
||||
<div css={{ position: 'relative' }} aria-busy={isLoading}>
|
||||
<EuiSwitch
|
||||
checked={enabled}
|
||||
disabled={isLoading}
|
||||
showLabel={false}
|
||||
label={enabled ? DISABLE_MONITOR_LABEL : ENABLE_MONITOR_LABEL}
|
||||
title={enabled ? DISABLE_MONITOR_LABEL : ENABLE_MONITOR_LABEL}
|
||||
data-test-subj="syntheticsIsMonitorEnabled"
|
||||
onChange={handleEnabledChange}
|
||||
/>
|
||||
{isLoading ? (
|
||||
<EuiProgress
|
||||
css={{ position: 'absolute', left: 0, bottom: -4, width: '100%', height: 2 }}
|
||||
size="xs"
|
||||
color="primary"
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ENABLE_MONITOR_LABEL = i18n.translate('xpack.uptime.monitorManagement.enableMonitorLabel', {
|
||||
defaultMessage: 'Enable monitor',
|
||||
});
|
||||
|
||||
const DISABLE_MONITOR_LABEL = i18n.translate('xpack.uptime.monitorManagement.disableMonitorLabel', {
|
||||
defaultMessage: 'Disable monitor',
|
||||
});
|
||||
|
||||
const getMonitorEnabledSuccessLabel = (name: string) =>
|
||||
i18n.translate('xpack.uptime.monitorManagement.monitorEnabledSuccessMessage', {
|
||||
defaultMessage: 'Monitor {name} enabled successfully.',
|
||||
values: { name },
|
||||
});
|
||||
|
||||
const getMonitorDisabledSuccessLabel = (name: string) =>
|
||||
i18n.translate('xpack.uptime.monitorManagement.monitorDisabledSuccessMessage', {
|
||||
defaultMessage: 'Monitor {name} disabled successfully.',
|
||||
values: { name },
|
||||
});
|
||||
|
||||
const getMonitorEnabledUpdateFailureMessage = (name: string) =>
|
||||
i18n.translate('xpack.uptime.monitorManagement.monitorEnabledUpdateFailureMessage', {
|
||||
defaultMessage: 'Unable to update monitor {name}.',
|
||||
values: { name },
|
||||
});
|
|
@ -7,12 +7,14 @@
|
|||
import React, { useContext, useMemo, useCallback } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiBasicTable, EuiPanel, EuiSpacer, EuiLink } from '@elastic/eui';
|
||||
import { SyntheticsMonitorSavedObject } from '../../../../common/types';
|
||||
import { MonitorManagementList as MonitorManagementListState } from '../../../state/reducers/monitor_management';
|
||||
import { MonitorFields } from '../../../../common/runtime_types';
|
||||
import { MonitorFields, SyntheticsMonitor } from '../../../../common/runtime_types';
|
||||
import { UptimeSettingsContext } from '../../../contexts';
|
||||
import { Actions } from './actions';
|
||||
import { MonitorLocations } from './monitor_locations';
|
||||
import { MonitorTags } from './tags';
|
||||
import { MonitorEnabled } from './monitor_enabled';
|
||||
import * as labels from '../../overview/monitor_list/translations';
|
||||
|
||||
interface Props {
|
||||
|
@ -32,7 +34,8 @@ export const MonitorManagementList = ({
|
|||
setPageSize,
|
||||
setPageIndex,
|
||||
}: Props) => {
|
||||
const { monitors, total, perPage, page: pageIndex } = list as MonitorManagementListState['list'];
|
||||
const { total, perPage, page: pageIndex } = list as MonitorManagementListState['list'];
|
||||
const monitors = list.monitors as SyntheticsMonitorSavedObject[];
|
||||
const { basePath } = useContext(UptimeSettingsContext);
|
||||
|
||||
const pagination = useMemo(
|
||||
|
@ -84,7 +87,7 @@ export const MonitorManagementList = ({
|
|||
name: i18n.translate('xpack.uptime.monitorManagement.monitorList.monitorType', {
|
||||
defaultMessage: 'Monitor type',
|
||||
}),
|
||||
render: ({ type }: Partial<MonitorFields>) => type,
|
||||
render: ({ type }: SyntheticsMonitor) => type,
|
||||
},
|
||||
{
|
||||
align: 'left' as const,
|
||||
|
@ -92,7 +95,7 @@ export const MonitorManagementList = ({
|
|||
name: i18n.translate('xpack.uptime.monitorManagement.monitorList.tags', {
|
||||
defaultMessage: 'Tags',
|
||||
}),
|
||||
render: ({ tags }: Partial<MonitorFields>) => (tags ? <MonitorTags tags={tags} /> : null),
|
||||
render: ({ tags }: SyntheticsMonitor) => (tags ? <MonitorTags tags={tags} /> : null),
|
||||
},
|
||||
{
|
||||
align: 'left' as const,
|
||||
|
@ -100,7 +103,7 @@ export const MonitorManagementList = ({
|
|||
name: i18n.translate('xpack.uptime.monitorManagement.monitorList.locations', {
|
||||
defaultMessage: 'Locations',
|
||||
}),
|
||||
render: ({ locations }: Partial<MonitorFields>) =>
|
||||
render: ({ locations }: SyntheticsMonitor) =>
|
||||
locations ? <MonitorLocations locations={locations} /> : null,
|
||||
},
|
||||
{
|
||||
|
@ -109,8 +112,7 @@ export const MonitorManagementList = ({
|
|||
name: i18n.translate('xpack.uptime.monitorManagement.monitorList.schedule', {
|
||||
defaultMessage: 'Schedule',
|
||||
}),
|
||||
render: ({ schedule }: Partial<MonitorFields>) =>
|
||||
`@every ${schedule?.number}${schedule?.unit}`,
|
||||
render: ({ schedule }: SyntheticsMonitor) => `@every ${schedule?.number}${schedule?.unit}`,
|
||||
},
|
||||
{
|
||||
align: 'left' as const,
|
||||
|
@ -118,9 +120,19 @@ export const MonitorManagementList = ({
|
|||
name: i18n.translate('xpack.uptime.monitorManagement.monitorList.URL', {
|
||||
defaultMessage: 'URL',
|
||||
}),
|
||||
render: (attributes: Partial<MonitorFields>) => attributes.urls || attributes.hosts,
|
||||
render: (attributes: MonitorFields) => attributes.urls || attributes.hosts,
|
||||
truncateText: true,
|
||||
},
|
||||
{
|
||||
align: 'left' as const,
|
||||
field: 'attributes',
|
||||
name: i18n.translate('xpack.uptime.monitorManagement.monitorList.enabled', {
|
||||
defaultMessage: 'Enabled',
|
||||
}),
|
||||
render: (attributes: SyntheticsMonitor, record: SyntheticsMonitorSavedObject) => (
|
||||
<MonitorEnabled id={record.id} monitor={attributes} setRefresh={setRefresh} />
|
||||
),
|
||||
},
|
||||
{
|
||||
align: 'left' as const,
|
||||
field: 'id',
|
||||
|
|
|
@ -24,7 +24,7 @@ export const MonitorManagementPage: React.FC = () => {
|
|||
useEffect(() => {
|
||||
if (refresh) {
|
||||
dispatch(getMonitors({ page: pageIndex, perPage: pageSize }));
|
||||
setRefresh(false);
|
||||
setRefresh(false); // TODO: avoid extra re-rendering when `refresh` turn to false (pass down the handler instead)
|
||||
}
|
||||
}, [dispatch, refresh, pageIndex, pageSize]);
|
||||
|
||||
|
|
|
@ -17,14 +17,13 @@ import {
|
|||
import { SyntheticsMonitorSavedObject } from '../../../common/types';
|
||||
import { apiService } from './utils';
|
||||
|
||||
// TODO: Type the return type from runtime types
|
||||
export const setMonitor = async ({
|
||||
monitor,
|
||||
id,
|
||||
}: {
|
||||
monitor: SyntheticsMonitor;
|
||||
id?: string;
|
||||
}): Promise<void> => {
|
||||
}): Promise<SyntheticsMonitorSavedObject> => {
|
||||
if (id) {
|
||||
return await apiService.put(`${API_URLS.SYNTHETICS_MONITORS}/${id}`, monitor);
|
||||
} else {
|
||||
|
@ -32,7 +31,6 @@ export const setMonitor = async ({
|
|||
}
|
||||
};
|
||||
|
||||
// TODO, change to monitor runtime type
|
||||
export const getMonitor = async ({ id }: { id: string }): Promise<SyntheticsMonitorSavedObject> => {
|
||||
return await apiService.get(`${API_URLS.SYNTHETICS_MONITORS}/${id}`);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue