mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Uptime][Monitor Management UI]Add sorting to Monitor Management monitors list table. (#124103)(#121117)
* Adds sorting to Monitor Management monitors list table. Default sorting is 'asc' on the 'name' column. * Fix column wrapping on monitor list table (Monitor Management UI). #121117
This commit is contained in:
parent
e5a8cec25e
commit
2c9f8a956c
11 changed files with 213 additions and 113 deletions
|
@ -10,6 +10,8 @@ import * as t from 'io-ts';
|
|||
export const FetchMonitorManagementListQueryArgsType = t.partial({
|
||||
page: t.number,
|
||||
perPage: t.number,
|
||||
sortField: t.string,
|
||||
sortOrder: t.union([t.literal('desc'), t.literal('asc')]),
|
||||
search: t.string,
|
||||
searchFields: t.array(t.string),
|
||||
});
|
||||
|
|
|
@ -18,11 +18,11 @@ import { spyOnUseFetcher } from '../../../lib/helper/spy_use_fetcher';
|
|||
import { Actions } from './actions';
|
||||
|
||||
describe('<Actions />', () => {
|
||||
const setRefresh = jest.fn();
|
||||
const onUpdate = jest.fn();
|
||||
const useFetcher = spyOnUseFetcher({});
|
||||
|
||||
it('navigates to edit monitor flow on edit pencil', () => {
|
||||
render(<Actions id="test-id" setRefresh={setRefresh} />);
|
||||
render(<Actions id="test-id" onUpdate={onUpdate} />);
|
||||
|
||||
expect(screen.getByLabelText('Edit monitor')).toHaveAttribute(
|
||||
'href',
|
||||
|
@ -34,7 +34,7 @@ describe('<Actions />', () => {
|
|||
useFetcher.mockImplementation(originalUseFetcher);
|
||||
const deleteMonitor = jest.spyOn(fetchers, 'deleteMonitor');
|
||||
const id = 'test-id';
|
||||
render(<Actions id={id} setRefresh={setRefresh} />);
|
||||
render(<Actions id={id} onUpdate={onUpdate} />);
|
||||
|
||||
expect(deleteMonitor).not.toBeCalled();
|
||||
|
||||
|
@ -45,11 +45,11 @@ describe('<Actions />', () => {
|
|||
|
||||
it('calls set refresh when deletion is successful', () => {
|
||||
const id = 'test-id';
|
||||
render(<Actions id={id} setRefresh={setRefresh} />);
|
||||
render(<Actions id={id} onUpdate={onUpdate} />);
|
||||
|
||||
userEvent.click(screen.getByLabelText('Delete monitor'));
|
||||
|
||||
expect(setRefresh).toBeCalledWith(true);
|
||||
expect(onUpdate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('shows loading spinner while waiting for monitor to delete', () => {
|
||||
|
@ -59,7 +59,7 @@ describe('<Actions />', () => {
|
|||
status: FETCH_STATUS.LOADING,
|
||||
refetch: () => {},
|
||||
});
|
||||
render(<Actions id={id} setRefresh={setRefresh} />);
|
||||
render(<Actions id={id} onUpdate={onUpdate} />);
|
||||
|
||||
expect(screen.getByLabelText('Deleting monitor...')).toBeInTheDocument();
|
||||
});
|
||||
|
|
|
@ -15,11 +15,11 @@ import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'
|
|||
|
||||
interface Props {
|
||||
id: string;
|
||||
setRefresh: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
isDisabled?: boolean;
|
||||
onUpdate: () => void;
|
||||
}
|
||||
|
||||
export const Actions = ({ id, setRefresh, isDisabled }: Props) => {
|
||||
export const Actions = ({ id, onUpdate, isDisabled }: Props) => {
|
||||
const [isDeleting, setIsDeleting] = useState<boolean>(false);
|
||||
const { basePath } = useContext(UptimeSettingsContext);
|
||||
|
||||
|
@ -46,13 +46,13 @@ export const Actions = ({ id, setRefresh, isDisabled }: Props) => {
|
|||
toastLifeTimeMs: 3000,
|
||||
});
|
||||
} else if (status === FETCH_STATUS.SUCCESS) {
|
||||
setRefresh(true);
|
||||
onUpdate();
|
||||
notifications.toasts.success({
|
||||
title: <p data-test-subj="uptimeDeleteMonitorSuccess">{MONITOR_DELETE_SUCCESS_LABEL}</p>,
|
||||
toastLifeTimeMs: 3000,
|
||||
});
|
||||
}
|
||||
}, [setIsDeleting, setRefresh, notifications.toasts, status]);
|
||||
}, [setIsDeleting, onUpdate, notifications.toasts, status]);
|
||||
|
||||
// TODO: Add popovers to icons
|
||||
return (
|
||||
|
|
|
@ -15,7 +15,7 @@ import { spyOnUseFetcher } from '../../../lib/helper/spy_use_fetcher';
|
|||
import { MonitorEnabled } from './monitor_enabled';
|
||||
|
||||
describe('<MonitorEnabled />', () => {
|
||||
const setRefresh = jest.fn();
|
||||
const onUpdate = jest.fn();
|
||||
const testMonitor = {
|
||||
[ConfigKey.MONITOR_TYPE]: DataStream.HTTP,
|
||||
[ConfigKey.ENABLED]: true,
|
||||
|
@ -34,7 +34,7 @@ describe('<MonitorEnabled />', () => {
|
|||
});
|
||||
|
||||
it('correctly renders "enabled" state', () => {
|
||||
render(<MonitorEnabled id="test-id" monitor={testMonitor} setRefresh={setRefresh} />);
|
||||
render(<MonitorEnabled id="test-id" monitor={testMonitor} onUpdate={onUpdate} />);
|
||||
|
||||
const switchButton = screen.getByRole('switch') as HTMLButtonElement;
|
||||
assertMonitorEnabled(switchButton);
|
||||
|
@ -45,7 +45,7 @@ describe('<MonitorEnabled />', () => {
|
|||
<MonitorEnabled
|
||||
id="test-id"
|
||||
monitor={{ ...testMonitor, [ConfigKey.ENABLED]: false }}
|
||||
setRefresh={setRefresh}
|
||||
onUpdate={onUpdate}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -54,7 +54,7 @@ describe('<MonitorEnabled />', () => {
|
|||
});
|
||||
|
||||
it('toggles on click', () => {
|
||||
render(<MonitorEnabled id="test-id" monitor={testMonitor} setRefresh={setRefresh} />);
|
||||
render(<MonitorEnabled id="test-id" monitor={testMonitor} onUpdate={onUpdate} />);
|
||||
|
||||
const switchButton = screen.getByRole('switch') as HTMLButtonElement;
|
||||
userEvent.click(switchButton);
|
||||
|
@ -70,7 +70,7 @@ describe('<MonitorEnabled />', () => {
|
|||
refetch: () => {},
|
||||
});
|
||||
|
||||
render(<MonitorEnabled id="test-id" monitor={testMonitor} setRefresh={setRefresh} />);
|
||||
render(<MonitorEnabled id="test-id" monitor={testMonitor} onUpdate={onUpdate} />);
|
||||
const switchButton = screen.getByRole('switch') as HTMLButtonElement;
|
||||
userEvent.click(switchButton);
|
||||
|
||||
|
|
|
@ -16,11 +16,11 @@ import { setMonitor } from '../../../state/api';
|
|||
interface Props {
|
||||
id: string;
|
||||
monitor: SyntheticsMonitor;
|
||||
setRefresh: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
onUpdate: () => void;
|
||||
isDisabled?: boolean;
|
||||
}
|
||||
|
||||
export const MonitorEnabled = ({ id, monitor, setRefresh, isDisabled }: Props) => {
|
||||
export const MonitorEnabled = ({ id, monitor, onUpdate, isDisabled }: Props) => {
|
||||
const [isEnabled, setIsEnabled] = useState<boolean | null>(null);
|
||||
|
||||
const { notifications } = useKibana();
|
||||
|
@ -53,7 +53,7 @@ export const MonitorEnabled = ({ id, monitor, setRefresh, isDisabled }: Props) =
|
|||
),
|
||||
toastLifeTimeMs: 3000,
|
||||
});
|
||||
setRefresh(true);
|
||||
onUpdate();
|
||||
}
|
||||
}, [status]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
|
|
|
@ -5,24 +5,24 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { ConfigKey, DataStream, HTTPFields, ScheduleUnit } from '../../../../common/runtime_types';
|
||||
import { render } from '../../../lib/helper/rtl_helpers';
|
||||
import { DataStream, HTTPFields, ScheduleUnit } from '../../../../common/runtime_types';
|
||||
import { MonitorManagementList } from './monitor_list';
|
||||
import { MonitorManagementList as MonitorManagementListState } from '../../../state/reducers/monitor_management';
|
||||
import { MonitorManagementList, MonitorManagementListPageState } from './monitor_list';
|
||||
|
||||
describe('<ActionBar />', () => {
|
||||
const setRefresh = jest.fn();
|
||||
const setPageSize = jest.fn();
|
||||
const setPageIndex = jest.fn();
|
||||
describe('<MonitorManagementList />', () => {
|
||||
const onUpdate = jest.fn();
|
||||
const onPageStateChange = jest.fn();
|
||||
const monitors = [];
|
||||
for (let i = 0; i < 12; i++) {
|
||||
monitors.push({
|
||||
id: `test-monitor-id-${i}`,
|
||||
attributes: {
|
||||
name: `test-monitor-${i}`,
|
||||
enabled: true,
|
||||
schedule: {
|
||||
unit: ScheduleUnit.MINUTES,
|
||||
number: `${i}`,
|
||||
|
@ -53,13 +53,20 @@ describe('<ActionBar />', () => {
|
|||
} as MonitorManagementListState,
|
||||
};
|
||||
|
||||
const pageState: MonitorManagementListPageState = {
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
sortField: ConfigKey.NAME,
|
||||
sortOrder: 'asc',
|
||||
};
|
||||
|
||||
it.each(monitors)('navigates to edit monitor flow on edit pencil', (monitor) => {
|
||||
render(
|
||||
<MonitorManagementList
|
||||
setRefresh={setRefresh}
|
||||
setPageSize={setPageSize}
|
||||
setPageIndex={setPageIndex}
|
||||
onUpdate={onUpdate}
|
||||
onPageStateChange={onPageStateChange}
|
||||
monitorList={state.monitorManagementList}
|
||||
pageState={pageState}
|
||||
/>,
|
||||
{ state }
|
||||
);
|
||||
|
@ -79,10 +86,10 @@ describe('<ActionBar />', () => {
|
|||
it('handles changing per page', () => {
|
||||
render(
|
||||
<MonitorManagementList
|
||||
setRefresh={setRefresh}
|
||||
setPageSize={setPageSize}
|
||||
setPageIndex={setPageIndex}
|
||||
onUpdate={onUpdate}
|
||||
onPageStateChange={onPageStateChange}
|
||||
monitorList={state.monitorManagementList}
|
||||
pageState={pageState}
|
||||
/>,
|
||||
{ state }
|
||||
);
|
||||
|
@ -91,23 +98,22 @@ describe('<ActionBar />', () => {
|
|||
|
||||
userEvent.click(screen.getByText('10 rows'));
|
||||
|
||||
expect(setPageSize).toBeCalledWith(10);
|
||||
expect(onPageStateChange).toBeCalledWith(expect.objectContaining({ pageSize: 10 }));
|
||||
});
|
||||
|
||||
it('handles refreshing and changing page when navigating to the next page', () => {
|
||||
render(
|
||||
it('handles refreshing and changing page when navigating to the next page', async () => {
|
||||
const { getByTestId } = render(
|
||||
<MonitorManagementList
|
||||
setRefresh={setRefresh}
|
||||
setPageSize={setPageSize}
|
||||
setPageIndex={setPageIndex}
|
||||
onUpdate={onUpdate}
|
||||
onPageStateChange={onPageStateChange}
|
||||
monitorList={state.monitorManagementList}
|
||||
pageState={{ ...pageState, pageSize: 3, pageIndex: 1 }}
|
||||
/>,
|
||||
{ state }
|
||||
);
|
||||
|
||||
userEvent.click(screen.getByTestId('pagination-button-next'));
|
||||
userEvent.click(getByTestId('pagination-button-next'));
|
||||
|
||||
expect(setPageIndex).toBeCalledWith(2);
|
||||
expect(setRefresh).toBeCalledWith(true);
|
||||
expect(onPageStateChange).toBeCalledWith(expect.objectContaining({ pageIndex: 2 }));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,139 +4,177 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React, { useContext, useMemo, useCallback } from 'react';
|
||||
import {
|
||||
Criteria,
|
||||
EuiBasicTable,
|
||||
EuiBasicTableColumn,
|
||||
EuiLink,
|
||||
EuiPanel,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import { EuiTableSortingType } from '@elastic/eui/src/components/basic_table/table_types';
|
||||
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, SyntheticsMonitor } from '../../../../common/runtime_types';
|
||||
import React, { useCallback, useContext, useMemo } from 'react';
|
||||
import {
|
||||
CommonFields,
|
||||
ConfigKey,
|
||||
FetchMonitorManagementListQueryArgs,
|
||||
ICMPSimpleFields,
|
||||
MonitorFields,
|
||||
ServiceLocations,
|
||||
SyntheticsMonitorWithId,
|
||||
TCPSimpleFields,
|
||||
} from '../../../../common/runtime_types';
|
||||
import { UptimeSettingsContext } from '../../../contexts';
|
||||
import { useBreakpoints } from '../../../hooks';
|
||||
import { MonitorManagementList as MonitorManagementListState } from '../../../state/reducers/monitor_management';
|
||||
import * as labels from '../../overview/monitor_list/translations';
|
||||
import { Actions } from './actions';
|
||||
import { MonitorEnabled } from './monitor_enabled';
|
||||
import { MonitorLocations } from './monitor_locations';
|
||||
import { MonitorTags } from './tags';
|
||||
import { MonitorEnabled } from './monitor_enabled';
|
||||
import * as labels from '../../overview/monitor_list/translations';
|
||||
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
export interface MonitorManagementListPageState {
|
||||
pageIndex: number;
|
||||
pageSize: number;
|
||||
sortField: keyof MonitorFields;
|
||||
sortOrder: NonNullable<FetchMonitorManagementListQueryArgs['sortOrder']>;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
setPageSize: React.Dispatch<React.SetStateAction<number>>;
|
||||
setPageIndex: React.Dispatch<React.SetStateAction<number>>;
|
||||
setRefresh: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
pageState: MonitorManagementListPageState;
|
||||
monitorList: MonitorManagementListState;
|
||||
onPageStateChange: (state: MonitorManagementListPageState) => void;
|
||||
onUpdate: () => void;
|
||||
}
|
||||
|
||||
export const MonitorManagementList = ({
|
||||
pageState: { pageIndex, pageSize, sortField, sortOrder },
|
||||
monitorList: {
|
||||
list,
|
||||
error: { monitorList: error },
|
||||
loading: { monitorList: loading },
|
||||
},
|
||||
setRefresh,
|
||||
setPageSize,
|
||||
setPageIndex,
|
||||
onPageStateChange,
|
||||
onUpdate,
|
||||
}: Props) => {
|
||||
const { total, perPage, page: pageIndex } = list as MonitorManagementListState['list'];
|
||||
const monitors = list.monitors as SyntheticsMonitorSavedObject[];
|
||||
const { basePath } = useContext(UptimeSettingsContext);
|
||||
const isXl = useBreakpoints().up('xl');
|
||||
|
||||
const pagination = useMemo(
|
||||
() => ({
|
||||
pageIndex: pageIndex - 1, // page index for EuiBasicTable is base 0
|
||||
pageSize: perPage,
|
||||
totalItemCount: total || 0,
|
||||
pageSizeOptions: [10, 25, 50, 100],
|
||||
}),
|
||||
[pageIndex, perPage, total]
|
||||
const { total } = list as MonitorManagementListState['list'];
|
||||
const monitors: SyntheticsMonitorWithId[] = useMemo(
|
||||
() => list.monitors.map((monitor) => ({ ...monitor.attributes, id: monitor.id })),
|
||||
[list.monitors]
|
||||
);
|
||||
|
||||
const handleOnChange = useCallback(
|
||||
({ page = {} }) => {
|
||||
({
|
||||
page = { index: 0, size: 10 },
|
||||
sort = { field: ConfigKey.NAME, direction: 'asc' },
|
||||
}: Criteria<SyntheticsMonitorWithId>) => {
|
||||
const { index, size } = page;
|
||||
const { field, direction } = sort;
|
||||
|
||||
setPageIndex(index + 1); // page index for Saved Objects is base 1
|
||||
setPageSize(size);
|
||||
setRefresh(true);
|
||||
onPageStateChange({
|
||||
pageIndex: index + 1, // page index for Saved Objects is base 1
|
||||
pageSize: size,
|
||||
sortField: field as keyof MonitorFields,
|
||||
sortOrder: direction,
|
||||
});
|
||||
},
|
||||
[setPageIndex, setPageSize, setRefresh]
|
||||
[onPageStateChange]
|
||||
);
|
||||
|
||||
const pagination = {
|
||||
pageIndex: pageIndex - 1, // page index for EuiBasicTable is base 0
|
||||
pageSize,
|
||||
totalItemCount: total || 0,
|
||||
pageSizeOptions: [10, 25, 50, 100],
|
||||
};
|
||||
|
||||
const sorting: EuiTableSortingType<SyntheticsMonitorWithId> = {
|
||||
sort: {
|
||||
field: sortField as keyof SyntheticsMonitorWithId,
|
||||
direction: sortOrder,
|
||||
},
|
||||
};
|
||||
|
||||
const canEdit: boolean = !!useKibana().services?.application?.capabilities.uptime.save;
|
||||
|
||||
const columns = [
|
||||
{
|
||||
align: 'left' as const,
|
||||
field: ConfigKey.NAME as string,
|
||||
name: i18n.translate('xpack.uptime.monitorManagement.monitorList.monitorName', {
|
||||
defaultMessage: 'Monitor name',
|
||||
}),
|
||||
render: ({
|
||||
attributes: { name },
|
||||
id,
|
||||
}: {
|
||||
attributes: Partial<MonitorFields>;
|
||||
id: string;
|
||||
}) => (
|
||||
sortable: true,
|
||||
render: (name: string, { id }: SyntheticsMonitorWithId) => (
|
||||
<EuiLink
|
||||
href={`${basePath}/app/uptime/monitor/${Buffer.from(id, 'utf8').toString('base64')}`}
|
||||
>
|
||||
{name}
|
||||
</EuiLink>
|
||||
),
|
||||
truncateText: true,
|
||||
},
|
||||
{
|
||||
align: 'left' as const,
|
||||
field: 'attributes',
|
||||
field: ConfigKey.MONITOR_TYPE,
|
||||
name: i18n.translate('xpack.uptime.monitorManagement.monitorList.monitorType', {
|
||||
defaultMessage: 'Monitor type',
|
||||
}),
|
||||
render: ({ type }: SyntheticsMonitor) => type,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
align: 'left' as const,
|
||||
field: 'attributes',
|
||||
field: ConfigKey.TAGS,
|
||||
name: i18n.translate('xpack.uptime.monitorManagement.monitorList.tags', {
|
||||
defaultMessage: 'Tags',
|
||||
}),
|
||||
render: ({ tags }: SyntheticsMonitor) => (tags ? <MonitorTags tags={tags} /> : null),
|
||||
render: (tags: string[]) => (tags ? <MonitorTags tags={tags} /> : null),
|
||||
},
|
||||
{
|
||||
align: 'left' as const,
|
||||
field: 'attributes',
|
||||
field: ConfigKey.LOCATIONS,
|
||||
name: i18n.translate('xpack.uptime.monitorManagement.monitorList.locations', {
|
||||
defaultMessage: 'Locations',
|
||||
}),
|
||||
render: ({ locations }: SyntheticsMonitor) =>
|
||||
render: (locations: ServiceLocations) =>
|
||||
locations ? <MonitorLocations locations={locations} /> : null,
|
||||
},
|
||||
{
|
||||
align: 'left' as const,
|
||||
field: 'attributes',
|
||||
field: ConfigKey.SCHEDULE,
|
||||
name: i18n.translate('xpack.uptime.monitorManagement.monitorList.schedule', {
|
||||
defaultMessage: 'Schedule',
|
||||
}),
|
||||
render: ({ schedule }: SyntheticsMonitor) => `@every ${schedule?.number}${schedule?.unit}`,
|
||||
render: (schedule: CommonFields[ConfigKey.SCHEDULE]) =>
|
||||
`@every ${schedule?.number}${schedule?.unit}`,
|
||||
},
|
||||
{
|
||||
align: 'left' as const,
|
||||
field: 'attributes',
|
||||
field: ConfigKey.URLS,
|
||||
name: i18n.translate('xpack.uptime.monitorManagement.monitorList.URL', {
|
||||
defaultMessage: 'URL',
|
||||
}),
|
||||
render: (attributes: MonitorFields) => attributes.urls || attributes.hosts,
|
||||
sortable: true,
|
||||
render: (urls: string, { hosts }: TCPSimpleFields | ICMPSimpleFields) => urls || hosts,
|
||||
truncateText: true,
|
||||
textOnly: true,
|
||||
},
|
||||
{
|
||||
align: 'left' as const,
|
||||
field: 'attributes',
|
||||
field: ConfigKey.ENABLED as string,
|
||||
name: i18n.translate('xpack.uptime.monitorManagement.monitorList.enabled', {
|
||||
defaultMessage: 'Enabled',
|
||||
}),
|
||||
render: (attributes: SyntheticsMonitor, record: SyntheticsMonitorSavedObject) => (
|
||||
render: (_enabled: boolean, monitor: SyntheticsMonitorWithId) => (
|
||||
<MonitorEnabled
|
||||
id={record.id}
|
||||
monitor={attributes}
|
||||
setRefresh={setRefresh}
|
||||
id={monitor.id}
|
||||
monitor={monitor}
|
||||
isDisabled={!canEdit}
|
||||
onUpdate={onUpdate}
|
||||
/>
|
||||
),
|
||||
},
|
||||
|
@ -146,9 +184,9 @@ export const MonitorManagementList = ({
|
|||
name: i18n.translate('xpack.uptime.monitorManagement.monitorList.actions', {
|
||||
defaultMessage: 'Actions',
|
||||
}),
|
||||
render: (id: string) => <Actions id={id} setRefresh={setRefresh} isDisabled={!canEdit} />,
|
||||
render: (id: string) => <Actions id={id} isDisabled={!canEdit} onUpdate={onUpdate} />,
|
||||
},
|
||||
];
|
||||
] as Array<EuiBasicTableColumn<SyntheticsMonitorWithId>>;
|
||||
|
||||
return (
|
||||
<EuiPanel hasBorder>
|
||||
|
@ -164,8 +202,9 @@ export const MonitorManagementList = ({
|
|||
itemId="monitor_id"
|
||||
items={monitors}
|
||||
columns={columns}
|
||||
tableLayout={'auto'}
|
||||
tableLayout={isXl ? 'auto' : 'fixed'}
|
||||
pagination={pagination}
|
||||
sorting={sorting}
|
||||
onChange={handleOnChange}
|
||||
noItemsMessage={loading ? labels.LOADING : labels.NO_DATA_MESSAGE}
|
||||
/>
|
||||
|
|
|
@ -22,13 +22,13 @@ export const MonitorLocations = ({ locations }: Props) => {
|
|||
const locationsToDisplay = locations.slice(0, toDisplay);
|
||||
|
||||
return (
|
||||
<EuiBadgeGroup>
|
||||
<EuiBadgeGroup css={{ width: '100%' }}>
|
||||
{locationsToDisplay.map((location: ServiceLocation) => (
|
||||
<EuiBadge
|
||||
key={location.id}
|
||||
color="hollow"
|
||||
className="eui-textTruncate"
|
||||
style={{ maxWidth: 120 }}
|
||||
css={{ display: 'flex', maxWidth: 120 }}
|
||||
>
|
||||
{location.label}
|
||||
</EuiBadge>
|
||||
|
|
|
@ -19,10 +19,15 @@ export const MonitorTags = ({ tags }: Props) => {
|
|||
const tagsToDisplay = tags.slice(0, toDisplay);
|
||||
|
||||
return (
|
||||
<EuiBadgeGroup>
|
||||
<EuiBadgeGroup css={{ width: '100%' }}>
|
||||
{tagsToDisplay.map((tag) => (
|
||||
// filtering only makes sense in monitor list, where we have summary
|
||||
<EuiBadge key={tag} color="hollow" className="eui-textTruncate" style={{ maxWidth: 120 }}>
|
||||
<EuiBadge
|
||||
key={tag}
|
||||
color="hollow"
|
||||
className="eui-textTruncate"
|
||||
css={{ display: 'flex', maxWidth: 120 }}
|
||||
>
|
||||
{tag}
|
||||
</EuiBadge>
|
||||
))}
|
||||
|
|
|
@ -5,37 +5,82 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useReducer, useCallback, Reducer } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useTrackPageview } from '../../../../observability/public';
|
||||
import { ConfigKey } from '../../../common/runtime_types';
|
||||
import { getMonitors } from '../../state/actions';
|
||||
import { monitorManagementListSelector } from '../../state/selectors';
|
||||
import { MonitorManagementList } from '../../components/monitor_management/monitor_list/monitor_list';
|
||||
import {
|
||||
MonitorManagementList,
|
||||
MonitorManagementListPageState,
|
||||
} from '../../components/monitor_management/monitor_list/monitor_list';
|
||||
import { useMonitorManagementBreadcrumbs } from './use_monitor_management_breadcrumbs';
|
||||
|
||||
export const MonitorManagementPage: React.FC = () => {
|
||||
const [refresh, setRefresh] = useState(true);
|
||||
const [pageIndex, setPageIndex] = useState(1); // saved objects page index is base 1
|
||||
const [pageSize, setPageSize] = useState(10); // saved objects page index is base 1
|
||||
const [pageState, dispatchPageAction] = useReducer<typeof monitorManagementPageReducer>(
|
||||
monitorManagementPageReducer,
|
||||
{
|
||||
pageIndex: 1, // saved objects page index is base 1
|
||||
pageSize: 10,
|
||||
sortOrder: 'asc',
|
||||
sortField: ConfigKey.NAME,
|
||||
}
|
||||
);
|
||||
|
||||
const onPageStateChange = useCallback(
|
||||
(state) => {
|
||||
dispatchPageAction({ type: 'update', payload: state });
|
||||
},
|
||||
[dispatchPageAction]
|
||||
);
|
||||
|
||||
const onUpdate = useCallback(() => {
|
||||
dispatchPageAction({ type: 'refresh' });
|
||||
}, [dispatchPageAction]);
|
||||
|
||||
useTrackPageview({ app: 'uptime', path: 'manage-monitors' });
|
||||
useTrackPageview({ app: 'uptime', path: 'manage-monitors', delay: 15000 });
|
||||
useMonitorManagementBreadcrumbs();
|
||||
const dispatch = useDispatch();
|
||||
const monitorList = useSelector(monitorManagementListSelector);
|
||||
|
||||
const { pageIndex, pageSize, sortField, sortOrder } = pageState as MonitorManagementListPageState;
|
||||
|
||||
useEffect(() => {
|
||||
if (refresh) {
|
||||
dispatch(getMonitors({ page: pageIndex, perPage: pageSize }));
|
||||
setRefresh(false);
|
||||
}
|
||||
}, [dispatch, refresh, pageIndex, pageSize]);
|
||||
dispatch(getMonitors({ page: pageIndex, perPage: pageSize, sortField, sortOrder }));
|
||||
}, [dispatch, pageState, pageIndex, pageSize, sortField, sortOrder]);
|
||||
|
||||
return (
|
||||
<MonitorManagementList
|
||||
pageState={pageState}
|
||||
monitorList={monitorList}
|
||||
setPageSize={setPageSize}
|
||||
setPageIndex={setPageIndex}
|
||||
setRefresh={setRefresh}
|
||||
onPageStateChange={onPageStateChange}
|
||||
onUpdate={onUpdate}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
type MonitorManagementPageAction =
|
||||
| {
|
||||
type: 'update';
|
||||
payload: MonitorManagementListPageState;
|
||||
}
|
||||
| { type: 'refresh' };
|
||||
|
||||
const monitorManagementPageReducer: Reducer<
|
||||
MonitorManagementListPageState,
|
||||
MonitorManagementPageAction
|
||||
> = (state: MonitorManagementListPageState, action: MonitorManagementPageAction) => {
|
||||
switch (action.type) {
|
||||
case 'update':
|
||||
return {
|
||||
...state,
|
||||
...action.payload,
|
||||
};
|
||||
case 'refresh':
|
||||
return { ...state };
|
||||
default:
|
||||
throw new Error(`Action "${(action as MonitorManagementPageAction)?.type}" not recognizable`);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -41,12 +41,13 @@ export const getAllSyntheticsMonitorRoute: UMRestApiRouteFactory = () => ({
|
|||
query: schema.object({
|
||||
page: schema.maybe(schema.number()),
|
||||
perPage: schema.maybe(schema.number()),
|
||||
sortField: schema.maybe(schema.string()),
|
||||
sortOrder: schema.maybe(schema.oneOf([schema.literal('desc'), schema.literal('asc')])),
|
||||
search: schema.maybe(schema.string()),
|
||||
}),
|
||||
},
|
||||
handler: async ({ request, savedObjectsClient }): Promise<any> => {
|
||||
const { perPage = 50, page, search } = request.query;
|
||||
|
||||
const { perPage = 50, page, sortField, sortOrder, search } = request.query;
|
||||
// TODO: add query/filtering params
|
||||
const {
|
||||
saved_objects: monitors,
|
||||
|
@ -56,6 +57,8 @@ export const getAllSyntheticsMonitorRoute: UMRestApiRouteFactory = () => ({
|
|||
type: syntheticsMonitorType,
|
||||
perPage,
|
||||
page,
|
||||
sortField,
|
||||
sortOrder,
|
||||
filter: search ? `${syntheticsMonitorType}.attributes.name: ${search}` : '',
|
||||
});
|
||||
return {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue