[Security Solution] Add validations for insight form in timeline and rules (#161034)

## Summary

Currently user can add a note in timeline with invalid markdown syntax
in insight forms. Same goes to the investigation guide in rule creation
-> About.

### Before
**In timeline -> Notes**

![image](33b05592-f97a-41d8-a394-fc111f4cc039)


**On rules -> about**

![image](ca09ac1a-7798-4b13-820b-85e5cf0f61c7)


### After
**Timeline -> Notes**
Add note button should be disabled if markdown is invalid

![image](d34b0ab1-c39b-443a-a309-00aba251731e)

**On rules -> about**
Field is red if markdown is invalid

![image](850f9f4a-71a7-44f8-a9e1-bbfbdb07b0dd)

### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
christineweng 2023-07-11 11:13:02 -05:00 committed by GitHub
parent 70ed200434
commit 08a57b9e3a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 49 additions and 12 deletions

View file

@ -30,6 +30,7 @@ interface MarkdownEditorProps {
dataTestSubj?: string;
height?: number;
autoFocusDisabled?: boolean;
setIsMarkdownInvalid: (value: boolean) => void;
}
type EuiMarkdownEditorRef = ElementRef<typeof EuiMarkdownEditor>;
@ -41,11 +42,27 @@ export interface MarkdownEditorRef {
}
const MarkdownEditorComponent = forwardRef<MarkdownEditorRef, MarkdownEditorProps>(
({ onChange, value, ariaLabel, editorId, dataTestSubj, height, autoFocusDisabled }, ref) => {
(
{
onChange,
value,
ariaLabel,
editorId,
dataTestSubj,
height,
autoFocusDisabled,
setIsMarkdownInvalid,
},
ref
) => {
const [markdownErrorMessages, setMarkdownErrorMessages] = useState([]);
const onParse = useCallback((err, { messages }) => {
setMarkdownErrorMessages(err ? [err] : messages);
}, []);
const onParse = useCallback(
(err, { messages }) => {
setMarkdownErrorMessages(err ? [err] : messages);
setIsMarkdownInvalid(err ? true : false);
},
[setIsMarkdownInvalid]
);
const editorRef = useRef<EuiMarkdownEditorRef>(null);
useEffect(() => {

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import React, { forwardRef } from 'react';
import React, { forwardRef, useState } from 'react';
import styled from 'styled-components';
import type { EuiMarkdownEditorProps } from '@elastic/eui';
import { EuiFormRow, EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
@ -34,6 +34,7 @@ export const MarkdownEditorForm = React.memo(
forwardRef<MarkdownEditorRef, MarkdownEditorFormProps>(
({ id, field, dataTestSubj, idAria, bottomRightContent }, ref) => {
const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field);
const [isMarkdownInvalid, setIsMarkdownInvalid] = useState(false);
return (
<EuiFormRow
@ -42,7 +43,7 @@ export const MarkdownEditorForm = React.memo(
error={errorMessage}
fullWidth
helpText={field.helpText}
isInvalid={isInvalid}
isInvalid={isInvalid || isMarkdownInvalid}
label={field.label}
labelAppend={field.labelAppend}
>
@ -54,6 +55,7 @@ export const MarkdownEditorForm = React.memo(
onChange={field.setValue}
value={field.value as string}
data-test-subj={`${dataTestSubj}-markdown-editor`}
setIsMarkdownInvalid={setIsMarkdownInvalid}
/>
{bottomRightContent && (
<BottomContentWrapper justifyContent={'flexEnd'}>

View file

@ -10,6 +10,7 @@ exports[`NewNote renders correctly 1`] = `
dataTestSubj="add-a-note"
height={200}
onChange={[MockFunction]}
setIsMarkdownInvalid={[MockFunction]}
value="The contents of a new note"
/>
</NewNoteTabs>

View file

@ -41,6 +41,7 @@ describe('AddNote', () => {
newNote: note,
onCancelAddNote: jest.fn(),
updateNewNote: jest.fn(),
setIsMarkdownInvalid: jest.fn(),
};
test('renders correctly', () => {

View file

@ -12,7 +12,7 @@ import {
EuiFlexItem,
EuiScreenReaderOnly,
} from '@elastic/eui';
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import styled from 'styled-components';
import { useDispatch } from 'react-redux';
@ -56,6 +56,7 @@ export const AddNote = React.memo<{
}>(({ associateNote, newNote, onCancelAddNote, updateNewNote, autoFocusDisabled = false }) => {
const dispatch = useDispatch();
const authenticatedUser = useCurrentUser();
const [isMarkdownInvalid, setIsMarkdownInvalid] = useState(false);
const updateNote = useCallback(
(note: Note) => dispatch(appActions.updateNote({ note })),
[dispatch]
@ -88,8 +89,8 @@ export const AddNote = React.memo<{
);
const isAddNoteDisabled = useMemo(() => {
return newNote.trim().length === 0;
}, [newNote]);
return newNote.trim().length === 0 || isMarkdownInvalid;
}, [newNote, isMarkdownInvalid]);
return (
<AddNotesContainer onKeyDown={onKeyDown} role="dialog">
@ -102,6 +103,7 @@ export const AddNote = React.memo<{
noteInputHeight={200}
updateNewNote={updateNewNote}
autoFocusDisabled={autoFocusDisabled}
setIsMarkdownInvalid={setIsMarkdownInvalid}
/>
<ButtonsContainer gutterSize="none">
{onCancelAddNote != null ? (

View file

@ -15,13 +15,25 @@ describe('NewNote', () => {
test('renders correctly', () => {
const wrapper = shallow(
<NewNote noteInputHeight={200} note={note} updateNewNote={jest.fn()} />
<NewNote
noteInputHeight={200}
note={note}
updateNewNote={jest.fn()}
setIsMarkdownInvalid={jest.fn()}
/>
);
expect(wrapper).toMatchSnapshot();
});
test('it renders a text area containing the contents of a new (raw) note', () => {
const wrapper = mount(<NewNote noteInputHeight={200} note={note} updateNewNote={jest.fn()} />);
const wrapper = mount(
<NewNote
noteInputHeight={200}
note={note}
updateNewNote={jest.fn()}
setIsMarkdownInvalid={jest.fn()}
/>
);
expect(
wrapper.find('[data-test-subj="add-a-note"] .euiMarkdownEditorDropZone').first().text()

View file

@ -25,7 +25,8 @@ export const NewNote = React.memo<{
note: string;
updateNewNote: UpdateInternalNewNote;
autoFocusDisabled?: boolean;
}>(({ note, noteInputHeight, updateNewNote, autoFocusDisabled = false }) => {
setIsMarkdownInvalid: (value: boolean) => void;
}>(({ note, noteInputHeight, updateNewNote, autoFocusDisabled = false, setIsMarkdownInvalid }) => {
return (
<NewNoteTabs data-test-subj="new-note-tabs">
<MarkdownEditor
@ -35,6 +36,7 @@ export const NewNote = React.memo<{
dataTestSubj="add-a-note"
height={noteInputHeight}
autoFocusDisabled={autoFocusDisabled}
setIsMarkdownInvalid={setIsMarkdownInvalid}
/>
</NewNoteTabs>
);