From 0a2424d585bf50caa952b9e078c2ba461dc8bf9f Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Thu, 2 Jul 2020 18:08:14 -0700 Subject: [PATCH] Fixed adding an extra space character on selecting alert variable in action text fields (#70028) * Fixed adding an extra space character on selecting alert variable in action text fields. * Made components for variables to be able to insert the variable by the cursor position * cleanup * Added variables support for all components * update on handle selections for text * Fixed functional tests --- .../components/add_message_variables.tsx | 1 + .../email/email_params.test.tsx | 4 +- .../email/email_params.tsx | 81 ++----- .../es_index/es_index_params.test.tsx | 2 +- .../es_index/es_index_params.tsx | 76 ++----- .../pagerduty/pagerduty_params.test.tsx | 2 +- .../pagerduty/pagerduty_params.tsx | 209 ++++-------------- .../server_log/server_log_params.test.tsx | 4 +- .../server_log/server_log_params.tsx | 47 +--- .../slack/slack_params.test.tsx | 8 +- .../slack/slack_params.tsx | 60 ++--- .../webhook/webhook_params.test.tsx | 8 +- .../webhook/webhook_params.tsx | 66 ++---- .../json_editor_with_message_variables.tsx | 86 +++++++ .../text_area_with_message_variables.tsx | 79 +++++++ .../text_field_with_message_variables.tsx | 71 ++++++ .../apps/triggers_actions_ui/alerts.ts | 12 +- 17 files changed, 397 insertions(+), 419 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_message_variables.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/text_field_with_message_variables.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/add_message_variables.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/add_message_variables.tsx index 957c79a5c5123..655f64995d147 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/add_message_variables.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/add_message_variables.tsx @@ -47,6 +47,7 @@ export const AddMessageVariables: React.FunctionComponent = ({ setIsVariablesPopoverOpen(true)} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.test.tsx index 053541d84c431..08616b2895a80 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.test.tsx @@ -28,7 +28,7 @@ describe('EmailParamsFields renders', () => { expect( wrapper.find('[data-test-subj="toEmailAddressInput"]').first().prop('selectedOptions') ).toStrictEqual([{ label: 'test@test.com' }]); - expect(wrapper.find('[data-test-subj="emailSubjectInput"]').length > 0).toBeTruthy(); - expect(wrapper.find('[data-test-subj="emailMessageInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="subjectInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="messageTextArea"]').length > 0).toBeTruthy(); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx index 58a9685fc73d9..39c59a10fbc81 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx @@ -5,11 +5,12 @@ */ import React, { Fragment, useState, useEffect } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiFieldText, EuiComboBox, EuiTextArea, EuiButtonEmpty, EuiFormRow } from '@elastic/eui'; +import { EuiComboBox, EuiButtonEmpty, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ActionParamsProps } from '../../../../types'; import { EmailActionParams } from '../types'; -import { AddMessageVariables } from '../../add_message_variables'; +import { TextFieldWithMessageVariables } from '../../text_field_with_message_variables'; +import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables'; export const EmailParamsFields = ({ actionParams, @@ -33,14 +34,6 @@ export const EmailParamsFields = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const onSelectMessageVariable = (paramsProperty: string, variable: string) => { - editAction( - paramsProperty, - ((actionParams as any)[paramsProperty] ?? '').concat(` {{${variable}}}`), - index - ); - }; - return ( - onSelectMessageVariable('subject', variable) - } - paramsProperty="subject" - /> - } > - 0 && subject !== undefined} - name="subject" - data-test-subj="emailSubjectInput" - value={subject || ''} - onChange={(e) => { - editAction('subject', e.target.value, index); - }} - onBlur={() => { - if (!subject) { - editAction('subject', '', index); - } - }} + - 0 && message !== undefined} + - onSelectMessageVariable('message', variable) - } - paramsProperty="message" - /> - } - > - 0 && message !== undefined} - value={message || ''} - name="message" - data-test-subj="emailMessageInput" - onChange={(e) => { - editAction('message', e.target.value, index); - }} - onBlur={() => { - if (!message) { - editAction('message', '', index); - } - }} - /> - + errors={errors.message as string[]} + /> ); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.test.tsx index 1f8bfde2cd22b..4a6c13bf7f1a6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.test.tsx @@ -20,7 +20,7 @@ describe('IndexParamsFields renders', () => { index={0} /> ); - expect(wrapper.find('[data-test-subj="actionIndexDoc"]').first().prop('value')).toBe(`{ + expect(wrapper.find('[data-test-subj="documentsJsonEditor"]').first().prop('value')).toBe(`{ "test": 123 }`); expect(wrapper.find('[data-test-subj="documentsAddVariableButton"]').length > 0).toBeTruthy(); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.tsx index 0b095cdc26984..fd6a3d64bd4be 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.tsx @@ -3,13 +3,11 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment } from 'react'; -import { EuiFormRow, EuiCodeEditor } from '@elastic/eui'; +import React from 'react'; import { i18n } from '@kbn/i18n'; -import { useXJsonMode } from '../../../../../../../../src/plugins/es_ui_shared/static/ace_x_json/hooks'; import { ActionParamsProps } from '../../../../types'; import { IndexActionParams } from '.././types'; -import { AddMessageVariables } from '../../add_message_variables'; +import { JsonEditorWithMessageVariables } from '../../json_editor_with_message_variables'; export const IndexParamsFields = ({ actionParams, @@ -18,62 +16,36 @@ export const IndexParamsFields = ({ messageVariables, }: ActionParamsProps) => { const { documents } = actionParams; - const { xJsonMode, convertToJson, setXJson, xJson } = useXJsonMode( - documents && documents.length > 0 ? documents[0] : null - ); - const onSelectMessageVariable = (variable: string) => { - const value = (xJson ?? '').concat(` {{${variable}}}`); - setXJson(value); - // Keep the documents in sync with the editor content - onDocumentsChange(convertToJson(value)); - }; - function onDocumentsChange(updatedDocuments: string) { + const onDocumentsChange = (updatedDocuments: string) => { try { const documentsJSON = JSON.parse(updatedDocuments); editAction('documents', [documentsJSON], index); // eslint-disable-next-line no-empty } catch (e) {} - } + }; + return ( - - onSelectMessageVariable(variable)} - paramsProperty="documents" - /> + 0 ? ((documents[0] as unknown) as string) : '' + } + label={i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.documentsFieldLabel', + { + defaultMessage: 'Document to index', + } + )} + aria-label={i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.jsonDocAriaLabel', + { + defaultMessage: 'Code editor', } - > - { - setXJson(xjson); - // Keep the documents in sync with the editor content - onDocumentsChange(convertToJson(xjson)); - }} - /> - - + )} + onDocumentsChange={onDocumentsChange} + /> ); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.test.tsx index 94bea3c51760d..1b26b1157add9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.test.tsx @@ -39,7 +39,7 @@ describe('PagerDutyParamsFields renders', () => { expect(wrapper.find('[data-test-subj="componentInput"]').length > 0).toBeTruthy(); expect(wrapper.find('[data-test-subj="groupInput"]').length > 0).toBeTruthy(); expect(wrapper.find('[data-test-subj="sourceInput"]').length > 0).toBeTruthy(); - expect(wrapper.find('[data-test-subj="pagerdutySummaryInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="summaryInput"]').length > 0).toBeTruthy(); expect(wrapper.find('[data-test-subj="dedupKeyAddVariableButton"]').length > 0).toBeTruthy(); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx index f0b131deb1490..c8ad5f5b7080e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ import React, { Fragment } from 'react'; -import { EuiFieldText, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSelect } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSelect } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ActionParamsProps } from '../../../../types'; import { PagerDutyActionParams } from '.././types'; -import { AddMessageVariables } from '../../add_message_variables'; +import { TextFieldWithMessageVariables } from '../../text_field_with_message_variables'; const PagerDutyParamsFields: React.FunctionComponent> = ({ actionParams, @@ -94,15 +94,6 @@ const PagerDutyParamsFields: React.FunctionComponent { - editAction( - paramsProperty, - ((actionParams as any)[paramsProperty] ?? '').concat(` {{${variable}}}`), - index - ); - }; - return ( @@ -159,29 +150,13 @@ const PagerDutyParamsFields: React.FunctionComponent - onSelectMessageVariable('dedupKey', variable) - } - paramsProperty="dedupKey" - /> - } > - ) => { - editAction('dedupKey', e.target.value, index); - }} - onBlur={() => { - if (!dedupKey) { - editAction('dedupKey', '', index); - } - }} + @@ -196,32 +171,14 @@ const PagerDutyParamsFields: React.FunctionComponent - onSelectMessageVariable('timestamp', variable) - } - paramsProperty="timestamp" - /> - } > - 0 && timestamp !== undefined} - onChange={(e: React.ChangeEvent) => { - editAction('timestamp', e.target.value, index); - }} - onBlur={() => { - if (timestamp?.trim()) { - editAction('timestamp', timestamp.trim(), index); - } else { - editAction('timestamp', '', index); - } - }} + @@ -234,29 +191,13 @@ const PagerDutyParamsFields: React.FunctionComponent - onSelectMessageVariable('component', variable) - } - paramsProperty="component" - /> - } > - ) => { - editAction('component', e.target.value, index); - }} - onBlur={() => { - if (!component) { - editAction('component', '', index); - } - }} + onSelectMessageVariable('group', variable)} - paramsProperty="group" - /> - } > - ) => { - editAction('group', e.target.value, index); - }} - onBlur={() => { - if (!group) { - editAction('group', '', index); - } - }} + onSelectMessageVariable('source', variable)} - paramsProperty="source" - /> - } > - ) => { - editAction('source', e.target.value, index); - }} - onBlur={() => { - if (!source) { - editAction('source', '', index); - } - }} + - onSelectMessageVariable('summary', variable) - } - paramsProperty="summary" - /> - } > - 0 && summary !== undefined} - name="summary" - value={summary || ''} - data-test-subj="pagerdutySummaryInput" - onChange={(e: React.ChangeEvent) => { - editAction('summary', e.target.value, index); - }} - onBlur={() => { - if (!summary) { - editAction('summary', '', index); - } - }} + onSelectMessageVariable('class', variable)} - paramsProperty="class" - /> - } > - ) => { - editAction('class', e.target.value, index); - }} - onBlur={() => { - if (!actionParams.class) { - editAction('class', '', index); - } - }} + diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.test.tsx index cb905023cae44..1849a7ec9817a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.test.tsx @@ -27,7 +27,7 @@ describe('ServerLogParamsFields renders', () => { expect( wrapper.find('[data-test-subj="loggingLevelSelect"]').first().prop('value') ).toStrictEqual('trace'); - expect(wrapper.find('[data-test-subj="loggingMessageInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="messageTextArea"]').length > 0).toBeTruthy(); }); test('level param field is rendered with default value if not selected', () => { @@ -47,6 +47,6 @@ describe('ServerLogParamsFields renders', () => { expect( wrapper.find('[data-test-subj="loggingLevelSelect"]').first().prop('value') ).toStrictEqual('info'); - expect(wrapper.find('[data-test-subj="loggingMessageInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="messageTextArea"]').length > 0).toBeTruthy(); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.tsx index c19aec2a993db..b79fa0ea94050 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.tsx @@ -5,10 +5,10 @@ */ import React, { Fragment, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiSelect, EuiTextArea, EuiFormRow } from '@elastic/eui'; +import { EuiSelect, EuiFormRow } from '@elastic/eui'; import { ActionParamsProps } from '../../../../types'; import { ServerLogActionParams } from '.././types'; -import { AddMessageVariables } from '../../add_message_variables'; +import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables'; export const ServerLogParamsFields: React.FunctionComponent { - editAction(paramsProperty, (message ?? '').concat(` {{${variable}}}`), index); - }; - return ( - 0 && message !== undefined} + - onSelectMessageVariable('message', variable) - } - paramsProperty="message" - /> - } - > - 0 && message !== undefined} - value={message || ''} - name="message" - data-test-subj="loggingMessageInput" - onChange={(e) => { - editAction('message', e.target.value, index); - }} - onBlur={() => { - if (!message) { - editAction('message', '', index); - } - }} - /> - + errors={errors.message as string[]} + /> ); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.test.tsx index 6efa8d64dafbe..8777c8f48e0f5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.test.tsx @@ -20,9 +20,9 @@ describe('SlackParamsFields renders', () => { index={0} /> ); - expect(wrapper.find('[data-test-subj="slackMessageTextArea"]').length > 0).toBeTruthy(); - expect( - wrapper.find('[data-test-subj="slackMessageTextArea"]').first().prop('value') - ).toStrictEqual('test message'); + expect(wrapper.find('[data-test-subj="messageTextArea"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="messageTextArea"]').first().prop('value')).toStrictEqual( + 'test message' + ); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.tsx index 5789d37a6bcf6..80a2f9d7709cc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.tsx @@ -3,12 +3,11 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, useEffect } from 'react'; -import { EuiTextArea, EuiFormRow } from '@elastic/eui'; +import React, { useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { ActionParamsProps } from '../../../../types'; import { SlackActionParams } from '../types'; -import { AddMessageVariables } from '../../add_message_variables'; +import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables'; const SlackParamsFields: React.FunctionComponent> = ({ actionParams, @@ -26,50 +25,21 @@ const SlackParamsFields: React.FunctionComponent { - editAction(paramsProperty, (message ?? '').concat(` {{${variable}}}`), index); - }; - return ( - - 0 && message !== undefined} - label={i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.messageTextAreaFieldLabel', - { - defaultMessage: 'Message', - } - )} - labelAppend={ - - onSelectMessageVariable('message', variable) - } - paramsProperty="message" - /> + - 0 && message !== undefined} - name="message" - value={message || ''} - data-test-subj="slackMessageTextArea" - onChange={(e) => { - editAction('message', e.target.value, index); - }} - onBlur={() => { - if (!message) { - editAction('message', '', index); - } - }} - /> - - + )} + errors={errors.message as string[]} + /> ); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.test.tsx index 665114bd86e9b..9e57d7ae608cc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.test.tsx @@ -20,10 +20,10 @@ describe('WebhookParamsFields renders', () => { index={0} /> ); - expect(wrapper.find('[data-test-subj="webhookBodyEditor"]').length > 0).toBeTruthy(); - expect( - wrapper.find('[data-test-subj="webhookBodyEditor"]').first().prop('value') - ).toStrictEqual('test message'); + expect(wrapper.find('[data-test-subj="bodyJsonEditor"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="bodyJsonEditor"]').first().prop('value')).toStrictEqual( + 'test message' + ); expect(wrapper.find('[data-test-subj="bodyAddVariableButton"]').length > 0).toBeTruthy(); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.tsx index 9e802b96e16be..1dfd9e3edc2c5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.tsx @@ -3,12 +3,11 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment } from 'react'; -import { EuiFormRow, EuiCodeEditor } from '@elastic/eui'; +import React from 'react'; import { i18n } from '@kbn/i18n'; import { ActionParamsProps } from '../../../../types'; import { WebhookActionParams } from '../types'; -import { AddMessageVariables } from '../../add_message_variables'; +import { JsonEditorWithMessageVariables } from '../../json_editor_with_message_variables'; const WebhookParamsFields: React.FunctionComponent> = ({ actionParams, @@ -18,49 +17,28 @@ const WebhookParamsFields: React.FunctionComponent { const { body } = actionParams; - const onSelectMessageVariable = (paramsProperty: string, variable: string) => { - editAction(paramsProperty, (body ?? '').concat(` {{${variable}}}`), index); - }; return ( - - 0 && body !== undefined} - fullWidth - error={errors.body} - labelAppend={ - onSelectMessageVariable('body', variable)} - paramsProperty="body" - /> + - { - editAction('body', json, index); - }} - /> - - + )} + aria-label={i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.bodyCodeEditorAriaLabel', + { + defaultMessage: 'Code editor', + } + )} + errors={errors.body as string[]} + onDocumentsChange={(json: string) => { + editAction('body', json, index); + }} + /> ); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.tsx new file mode 100644 index 0000000000000..2aac389dce5ec --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.tsx @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { useState } from 'react'; +import { EuiCodeEditor, EuiFormRow } from '@elastic/eui'; +import './add_message_variables.scss'; +import { useXJsonMode } from '../../../../../../src/plugins/es_ui_shared/static/ace_x_json/hooks'; + +import { AddMessageVariables } from './add_message_variables'; + +interface Props { + messageVariables: string[] | undefined; + paramsProperty: string; + inputTargetValue: string; + label: string; + errors?: string[]; + areaLabel?: string; + onDocumentsChange: (data: string) => void; +} + +export const JsonEditorWithMessageVariables: React.FunctionComponent = ({ + messageVariables, + paramsProperty, + inputTargetValue, + label, + errors, + areaLabel, + onDocumentsChange, +}) => { + const [cursorPosition, setCursorPosition] = useState(null); + + const { xJsonMode, convertToJson, setXJson, xJson } = useXJsonMode(inputTargetValue ?? null); + + const onSelectMessageVariable = (variable: string) => { + const templatedVar = `{{${variable}}}`; + let newValue = ''; + if (cursorPosition) { + const cursor = cursorPosition.getCursor(); + cursorPosition.session.insert(cursor, templatedVar); + newValue = cursorPosition.session.getValue(); + } else { + newValue = templatedVar; + } + setXJson(newValue); + // Keep the documents in sync with the editor content + onDocumentsChange(convertToJson(newValue)); + }; + + const onClickWithMessageVariable = (_value: any) => { + setCursorPosition(_value); + }; + + return ( + 0 && inputTargetValue !== undefined} + label={label} + labelAppend={ + onSelectMessageVariable(variable)} + paramsProperty={paramsProperty} + /> + } + > + { + setXJson(xjson); + // Keep the documents in sync with the editor content + onDocumentsChange(convertToJson(xjson)); + }} + onCursorChange={(_value: any) => onClickWithMessageVariable(_value)} + /> + + ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_message_variables.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_message_variables.tsx new file mode 100644 index 0000000000000..0b8a9349ad5fb --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_message_variables.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { useState } from 'react'; +import { EuiTextArea, EuiFormRow } from '@elastic/eui'; +import './add_message_variables.scss'; +import { AddMessageVariables } from './add_message_variables'; + +interface Props { + messageVariables: string[] | undefined; + paramsProperty: string; + index: number; + inputTargetValue?: string; + editAction: (property: string, value: any, index: number) => void; + label: string; + errors?: string[]; +} + +export const TextAreaWithMessageVariables: React.FunctionComponent = ({ + messageVariables, + paramsProperty, + index, + inputTargetValue, + editAction, + label, + errors, +}) => { + const [currentTextElement, setCurrentTextElement] = useState(null); + + const onSelectMessageVariable = (variable: string) => { + const templatedVar = `{{${variable}}}`; + const startPosition = currentTextElement?.selectionStart ?? 0; + const endPosition = currentTextElement?.selectionEnd ?? 0; + const newValue = + (inputTargetValue ?? '').substring(0, startPosition) + + templatedVar + + (inputTargetValue ?? '').substring(endPosition, (inputTargetValue ?? '').length); + editAction(paramsProperty, newValue, index); + }; + + const onChangeWithMessageVariable = (e: React.ChangeEvent) => { + editAction(paramsProperty, e.target.value, index); + }; + + return ( + 0 && inputTargetValue !== undefined} + label={label} + labelAppend={ + onSelectMessageVariable(variable)} + paramsProperty={paramsProperty} + /> + } + > + 0 && inputTargetValue !== undefined} + name={paramsProperty} + value={inputTargetValue} + data-test-subj={`${paramsProperty}TextArea`} + onChange={(e: React.ChangeEvent) => onChangeWithMessageVariable(e)} + onFocus={(e: React.FocusEvent) => { + setCurrentTextElement(e.target); + }} + onBlur={() => { + if (!inputTargetValue) { + editAction(paramsProperty, '', index); + } + }} + /> + + ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/text_field_with_message_variables.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/text_field_with_message_variables.tsx new file mode 100644 index 0000000000000..e280fd3f34e99 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/text_field_with_message_variables.tsx @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { useState } from 'react'; +import { EuiFieldText } from '@elastic/eui'; +import './add_message_variables.scss'; +import { AddMessageVariables } from './add_message_variables'; + +interface Props { + messageVariables: string[] | undefined; + paramsProperty: string; + index: number; + inputTargetValue?: string; + editAction: (property: string, value: any, index: number) => void; + errors?: string[]; +} + +export const TextFieldWithMessageVariables: React.FunctionComponent = ({ + messageVariables, + paramsProperty, + index, + inputTargetValue, + editAction, + errors, +}) => { + const [currentTextElement, setCurrentTextElement] = useState(null); + + const onSelectMessageVariable = (variable: string) => { + const templatedVar = `{{${variable}}}`; + const startPosition = currentTextElement?.selectionStart ?? 0; + const endPosition = currentTextElement?.selectionEnd ?? 0; + const newValue = + (inputTargetValue ?? '').substring(0, startPosition) + + templatedVar + + (inputTargetValue ?? '').substring(endPosition, (inputTargetValue ?? '').length); + editAction(paramsProperty, newValue, index); + }; + + const onChangeWithMessageVariable = (e: React.ChangeEvent) => { + editAction(paramsProperty, e.target.value, index); + }; + + return ( + 0 && inputTargetValue !== undefined} + data-test-subj={`${paramsProperty}Input`} + value={inputTargetValue} + onChange={(e: React.ChangeEvent) => onChangeWithMessageVariable(e)} + onFocus={(e: React.FocusEvent) => { + setCurrentTextElement(e.target); + }} + onBlur={(e: React.FocusEvent) => { + if (!inputTargetValue) { + editAction(paramsProperty, '', index); + } + }} + append={ + onSelectMessageVariable(variable)} + paramsProperty={paramsProperty} + /> + } + /> + ); +}; diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts index 8a50f7b3be2fe..e05bfa7459492 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts @@ -75,9 +75,19 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await find.clickByCssSelector('[data-test-subj="saveActionButtonModal"]:not(disabled)'); const createdConnectorToastTitle = await pageObjects.common.closeToast(); expect(createdConnectorToastTitle).to.eql(`Created '${slackConnectorName}'`); - await testSubjects.setValue('slackMessageTextArea', 'test message'); + await testSubjects.setValue('messageTextArea', 'test message '); await testSubjects.click('messageAddVariableButton'); await testSubjects.click('variableMenuButton-0'); + const messageTextArea = await find.byCssSelector('[data-test-subj="messageTextArea"]'); + expect(await messageTextArea.getAttribute('value')).to.eql('test message {{alertId}}'); + await messageTextArea.type(' some additional text '); + + await testSubjects.click('messageAddVariableButton'); + await testSubjects.click('variableMenuButton-1'); + + expect(await messageTextArea.getAttribute('value')).to.eql( + 'test message {{alertId}} some additional text {{alertName}}' + ); await testSubjects.click('saveAlertButton'); const toastTitle = await pageObjects.common.closeToast();