diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/circle.test.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/circle.test.tsx new file mode 100644 index 0000000000000..e29bb2ac6e92e --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/circle.test.tsx @@ -0,0 +1,114 @@ +/* + * 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 { act } from 'react-dom/test-utils'; +import { setup, SetupResult, getProcessorValue } from './processor.helpers'; + +const CIRCLE_TYPE = 'circle'; + +describe('Processor: Circle', () => { + let onUpdate: jest.Mock; + let testBed: SetupResult; + + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + beforeEach(async () => { + onUpdate = jest.fn(); + + await act(async () => { + testBed = await setup({ + value: { + processors: [], + }, + onFlyoutOpen: jest.fn(), + onUpdate, + }); + }); + testBed.component.update(); + const { + actions: { addProcessor, addProcessorType }, + } = testBed; + // Open the processor flyout + addProcessor(); + + // Add type (the other fields are not visible until a type is selected) + await addProcessorType(CIRCLE_TYPE); + }); + + test('prevents form submission if required fields are not provided', async () => { + const { + actions: { saveNewProcessor }, + form, + } = testBed; + + // Click submit button with only the type defined + await saveNewProcessor(); + + // Expect form error as "field" and "shape_type" are required parameters + expect(form.getErrorsMessages()).toEqual([ + 'A field value is required.', + 'A shape type value is required.', + ]); + }); + + test('saves with required parameter values', async () => { + const { + actions: { saveNewProcessor }, + form, + } = testBed; + + // Add "field" value (required) + form.setInputValue('fieldNameField.input', 'field_1'); + // Save the field + form.setSelectValue('shapeSelectorField', 'shape'); + // Set the error distance + form.setInputValue('errorDistanceField.input', '10'); + + await saveNewProcessor(); + + const processors = getProcessorValue(onUpdate, CIRCLE_TYPE); + + expect(processors[0].circle).toEqual({ + field: 'field_1', + error_distance: 10, + shape_type: 'shape', + }); + }); + + test('allows optional parameters to be set', async () => { + const { + actions: { saveNewProcessor }, + form, + } = testBed; + + // Add "field" value (required) + form.setInputValue('fieldNameField.input', 'field_1'); + // Select the shape + form.setSelectValue('shapeSelectorField', 'geo_shape'); + // Add "target_field" value + form.setInputValue('targetField.input', 'target_field'); + + form.setInputValue('errorDistanceField.input', '10'); + + // Save the field with new changes + await saveNewProcessor(); + + const processors = getProcessorValue(onUpdate, CIRCLE_TYPE); + expect(processors[0].circle).toEqual({ + field: 'field_1', + error_distance: 10, + shape_type: 'geo_shape', + target_field: 'target_field', + }); + }); +}); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx index c00f09b2d2b06..15e8c323b1308 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx @@ -151,6 +151,8 @@ type TestSubject = | 'keepOriginalField.input' | 'removeIfSuccessfulField.input' | 'targetFieldsField.input' + | 'shapeSelectorField' + | 'errorDistanceField.input' | 'separatorValueField.input' | 'quoteValueField.input' | 'emptyValueField.input' diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/circle.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/circle.tsx index acb480df6d35f..74a7f37d841ae 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/circle.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/circle.tsx @@ -97,6 +97,7 @@ export const Circle: FunctionComponent = () => { /> { { if (!valid) { return ( - - } - color="danger" - iconType="help" - > - {message}{' '} - - - - + + + + + } + body={

{message}

} + actions={[ + + + , + ]} + /> +
); } return ( diff --git a/x-pack/plugins/watcher/public/application/components/page_error/page_error.tsx b/x-pack/plugins/watcher/public/application/components/page_error/page_error.tsx index ca05d390518f2..321b5c0e5e11b 100644 --- a/x-pack/plugins/watcher/public/application/components/page_error/page_error.tsx +++ b/x-pack/plugins/watcher/public/application/components/page_error/page_error.tsx @@ -25,7 +25,7 @@ export function getPageErrorCode(errorOrErrors: any) { } } -export function PageError({ errorCode, id }: { errorCode?: any; id?: any }) { +export function PageError({ errorCode, id }: { errorCode?: number; id?: string }) { switch (errorCode) { case 404: return ; diff --git a/x-pack/plugins/watcher/public/application/components/page_error/page_error_forbidden.tsx b/x-pack/plugins/watcher/public/application/components/page_error/page_error_forbidden.tsx index c2e93c7f06600..56dc5c7dc22b5 100644 --- a/x-pack/plugins/watcher/public/application/components/page_error/page_error_forbidden.tsx +++ b/x-pack/plugins/watcher/public/application/components/page_error/page_error_forbidden.tsx @@ -13,8 +13,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; export function PageErrorForbidden() { return ( - + {id ? ( + + ) : ( + + )}

} /> diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/json_watch_edit.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/json_watch_edit.tsx index 8b5827fbd0fe0..80931c3f60c05 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/json_watch_edit.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/json_watch_edit.tsx @@ -7,15 +7,7 @@ import React, { useContext, useState } from 'react'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiPageContent, - EuiSpacer, - EuiTab, - EuiTabs, - EuiTitle, -} from '@elastic/eui'; +import { EuiPageHeader, EuiSpacer, EuiPageContentBody } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ExecuteDetails } from '../../../../models/execute_details'; import { getActionType } from '../../../../../../common/lib/get_action_type'; @@ -96,36 +88,31 @@ export const JsonWatchEdit = ({ pageTitle }: { pageTitle: string }) => { const hasExecuteWatchErrors = !!Object.keys(executeWatchErrors).find( (errorKey) => executeWatchErrors[errorKey].length >= 1 ); + return ( - - - - -

{pageTitle}

-
-
-
- - {WATCH_TABS.map((tab, index) => ( - { - setSelectedTab(tab.id); - setExecuteDetails( - new ExecuteDetails({ - ...executeDetails, - actionModes: getActionModes(watchActions), - }) - ); - }} - isSelected={tab.id === selectedTab} - key={index} - data-test-subj="tab" - > - {tab.name} - - ))} - + + {pageTitle}} + bottomBorder + tabs={WATCH_TABS.map((tab, index) => ({ + onClick: () => { + setSelectedTab(tab.id); + setExecuteDetails( + new ExecuteDetails({ + ...executeDetails, + actionModes: getActionModes(watchActions), + }) + ); + }, + isSelected: tab.id === selectedTab, + key: index, + 'data-test-subj': 'tab', + label: tab.name, + }))} + /> + + {selectedTab === WATCH_SIMULATE_TAB && ( { watchActions={watchActions} /> )} + {selectedTab === WATCH_EDIT_TAB && } -
+ ); }; diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/monitoring_watch_edit/monitoring_watch_edit.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/monitoring_watch_edit/monitoring_watch_edit.tsx index 930c11340ce5e..b00e4dc310e27 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/monitoring_watch_edit/monitoring_watch_edit.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/monitoring_watch_edit/monitoring_watch_edit.tsx @@ -7,16 +7,7 @@ import React, { useContext } from 'react'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiPageContent, - EuiSpacer, - EuiTitle, - EuiCallOut, - EuiText, - EuiLink, -} from '@elastic/eui'; +import { EuiPageContent, EuiEmptyPrompt, EuiLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { WatchContext } from '../../watch_context'; import { useAppContext } from '../../../../app_context'; @@ -27,46 +18,31 @@ export const MonitoringWatchEdit = ({ pageTitle }: { pageTitle: string }) => { const { watch } = useContext(WatchContext); const { history } = useAppContext(); - const systemWatchTitle = ( - - ); - const systemWatchMessage = ( - - - ), }} /> ); return ( - - - - -

{pageTitle}

-
-
-
- - - -

{systemWatchMessage}

-
-
+ + {pageTitle}} + body={

{systemWatchMessage}

} + actions={[ + + + , + ]} + />
); }; diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.tsx index 2f89a3bc2be64..6587974363a80 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.tsx @@ -18,13 +18,14 @@ import { EuiFlexGroup, EuiFlexItem, EuiForm, - EuiPageContent, EuiPopover, EuiPopoverTitle, EuiSelect, EuiSpacer, EuiText, EuiTitle, + EuiPageHeader, + EuiPageContentBody, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -236,19 +237,15 @@ export const ThresholdWatchEdit = ({ pageTitle }: { pageTitle: string }) => { }; return ( - - - - -

{pageTitle}

-
- - - {watch.titleDescription} - -
-
- + + {pageTitle}} + description={watch.titleDescription} + bottomBorder + /> + + + {serverError && ( @@ -957,6 +954,6 @@ export const ThresholdWatchEdit = ({ pageTitle }: { pageTitle: string }) => { close={() => setIsRequestVisible(false)} /> ) : null} -
+ ); }; diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/watch_edit.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/watch_edit.tsx index 525ae077df655..fa3c7e374f7b5 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/watch_edit.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/watch_edit.tsx @@ -10,19 +10,20 @@ import { isEqual } from 'lodash'; import { EuiPageContent } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; - import { FormattedMessage } from '@kbn/i18n/react'; -import { Watch } from '../../../models/watch'; + import { WATCH_TYPES } from '../../../../../common/constants'; import { BaseWatch } from '../../../../../common/types/watch_types'; -import { getPageErrorCode, PageError, SectionLoading, SectionError } from '../../../components'; +import { getPageErrorCode, PageError, SectionLoading } from '../../../components'; import { loadWatch } from '../../../lib/api'; import { listBreadcrumb, editBreadcrumb, createBreadcrumb } from '../../../lib/breadcrumbs'; +import { useAppContext } from '../../../app_context'; +import { Watch } from '../../../models/watch'; +import { PageError as GenericPageError } from '../../../shared_imports'; +import { WatchContext } from '../watch_context'; import { JsonWatchEdit } from './json_watch_edit'; import { ThresholdWatchEdit } from './threshold_watch_edit'; import { MonitoringWatchEdit } from './monitoring_watch_edit'; -import { WatchContext } from '../watch_context'; -import { useAppContext } from '../../../app_context'; const getTitle = (watch: BaseWatch) => { if (watch.isNew) { @@ -115,7 +116,7 @@ export const WatchEdit = ({ const loadedWatch = await loadWatch(id); dispatch({ command: 'setWatch', payload: loadedWatch }); } catch (error) { - dispatch({ command: 'setError', payload: error }); + dispatch({ command: 'setError', payload: error.body }); } } else if (type) { const WatchType = Watch.getWatchTypes()[type]; @@ -135,36 +136,34 @@ export const WatchEdit = ({ const errorCode = getPageErrorCode(loadError); if (errorCode) { return ( - + ); - } - - if (loadError) { + } else if (loadError) { return ( - - - } - error={loadError} - /> - + + } + error={loadError} + /> ); } if (!watch) { return ( - - - + + + + + ); } diff --git a/x-pack/plugins/watcher/public/application/sections/watch_list/components/watch_list.tsx b/x-pack/plugins/watcher/public/application/sections/watch_list/components/watch_list.tsx index 0e89871063507..31accef0b6369 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_list/components/watch_list.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_list/components/watch_list.tsx @@ -11,25 +11,25 @@ import { CriteriaWithPagination, EuiButton, EuiButtonEmpty, - EuiFlexGroup, - EuiFlexItem, EuiInMemoryTable, EuiLink, EuiPageContent, EuiSpacer, EuiText, - EuiTitle, EuiToolTip, EuiEmptyPrompt, EuiButtonIcon, EuiPopover, EuiContextMenuPanel, EuiContextMenuItem, + EuiPageHeader, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { Moment } from 'moment'; +import { reactRouterNavigate } from '../../../../../../../../src/plugins/kibana_react/public'; + import { REFRESH_INTERVALS, PAGINATION, WATCH_TYPES } from '../../../../../common/constants'; import { listBreadcrumb } from '../../../lib/breadcrumbs'; import { @@ -37,15 +37,13 @@ import { PageError, DeleteWatchesModal, WatchStatus, - SectionError, SectionLoading, Error, } from '../../../components'; import { useLoadWatches } from '../../../lib/api'; import { goToCreateThresholdAlert, goToCreateAdvancedWatch } from '../../../lib/navigation'; import { useAppContext } from '../../../app_context'; - -import { reactRouterNavigate } from '../../../../../../../../src/plugins/kibana_react/public'; +import { PageError as GenericPageError } from '../../../shared_imports'; export const WatchList = () => { // hooks @@ -173,21 +171,36 @@ export const WatchList = () => { if (isWatchesLoading) { return ( - - - + + + + + ); } - if (getPageErrorCode(error)) { + const errorCode = getPageErrorCode(error); + if (errorCode) { return ( - - + + ); + } else if (error) { + return ( + + } + error={(error as unknown) as Error} + /> + ); } if (availableWatches && availableWatches.length === 0) { @@ -206,7 +219,7 @@ export const WatchList = () => { ); return ( - + { let content; - if (error) { - content = ( - - } - error={(error as unknown) as Error} - /> - ); - } else if (availableWatches) { + if (availableWatches) { const columns = [ { field: 'id', @@ -463,56 +464,46 @@ export const WatchList = () => { ); } - if (content) { - return ( - - { - if (deleted) { - setDeletedWatches([...deletedWatches, ...watchesToDelete]); - } - setWatchesToDelete([]); - }} - watchesToDelete={watchesToDelete} - /> - - - - -

- -

-
- - - - - -
-
- - - - -

{watcherDescriptionText}

-
+ return ( + <> + + + + } + bottomBorder + rightSideItems={[ + + + , + ]} + description={watcherDescriptionText} + /> + { + if (deleted) { + setDeletedWatches([...deletedWatches, ...watchesToDelete]); + } + setWatchesToDelete([]); + }} + watchesToDelete={watchesToDelete} + /> - + - {content} -
- ); - } - return null; + {content} + + ); }; diff --git a/x-pack/plugins/watcher/public/application/sections/watch_status/components/watch_status.tsx b/x-pack/plugins/watcher/public/application/sections/watch_status/components/watch_status.tsx index 1e3548620339a..73400b9ccaaa7 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_status/components/watch_status.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_status/components/watch_status.tsx @@ -9,14 +9,10 @@ import React, { useEffect, useState } from 'react'; import { EuiPageContent, EuiSpacer, - EuiTabs, - EuiTab, - EuiFlexGroup, - EuiFlexItem, - EuiTitle, EuiToolTip, EuiBadge, EuiButtonEmpty, + EuiPageHeader, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -88,18 +84,20 @@ export const WatchStatus = ({ if (isWatchDetailLoading) { return ( - - - + + + + + ); } if (errorCode) { return ( - + ); @@ -156,20 +154,11 @@ export const WatchStatus = ({ return ( - - { - if (deleted) { - goToWatchList(); - } - setWatchesToDelete([]); - }} - watchesToDelete={watchesToDelete} - /> - - - -

+ <> + + -

-
-
- {isSystemWatch ? ( - - - } - > - - - - - - ) : ( - - - + + {isSystemWatch && ( + <> + {' '} + + } + > + + + + + + )} + + } + bottomBorder + rightSideItems={ + isSystemWatch + ? [] + : [ toggleWatchActivation()} isLoading={isTogglingActivation} > {activationButtonText} - - - + , { @@ -223,30 +213,34 @@ export const WatchStatus = ({ id="xpack.watcher.sections.watchHistory.deleteWatchButtonLabel" defaultMessage="Delete" /> - - - - - )} -
- - - {WATCH_STATUS_TABS.map((tab, index) => ( - { - setSelectedTab(tab.id); - }} - isSelected={tab.id === selectedTab} - key={index} - data-test-subj="tab" - > - {tab.name} - - ))} - + , + ] + } + tabs={WATCH_STATUS_TABS.map((tab, index) => ({ + onClick: () => { + setSelectedTab(tab.id); + }, + isSelected: tab.id === selectedTab, + key: index, + 'data-test-subj': 'tab', + label: tab.name, + }))} + /> + + {selectedTab === WATCH_ACTIONS_TAB ? : } -
+ + { + if (deleted) { + goToWatchList(); + } + setWatchesToDelete([]); + }} + watchesToDelete={watchesToDelete} + /> +
); } diff --git a/x-pack/plugins/watcher/public/application/shared_imports.ts b/x-pack/plugins/watcher/public/application/shared_imports.ts index e3eb11eda77b3..44bef3b0c4f5f 100644 --- a/x-pack/plugins/watcher/public/application/shared_imports.ts +++ b/x-pack/plugins/watcher/public/application/shared_imports.ts @@ -12,4 +12,5 @@ export { sendRequest, useRequest, XJson, + PageError, } from '../../../../../src/plugins/es_ui_shared/public'; diff --git a/yarn.lock b/yarn.lock index 68d27e955aa12..61c5305821a05 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20304,9 +20304,9 @@ normalize-url@^3.0.0: integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== normalize-url@^4.1.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" - integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== now-and-later@^2.0.0: version "2.0.0"