[SIEM][Case] Merge header components (#57816) (#58043)

* Create edit node component

* Accept EditNodeComponent

* Switch to old header

* test

* Remove iconType

* Remove isEditTitle

* Move translations

* Delete header_page_new component

* Move editable title component to different folder

* Update jest snapshot

* Rename prop

* Make EditableTitleComponent more generic

* useCallback instead of inline functions in props

* Hardcode titles

* Move UI state inside EditableTitleComponent

* Seperate title's tests

* Create tests for EditableTitleComponent

* useCallbacks on EditableTitle component

* Create translation for aria-label in edit icon

* Check if switched to edit mode after pressing submit

* Test title when canceled

Co-authored-by: patrykkopycinski <contact@patrykkopycinski.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>

Co-authored-by: patrykkopycinski <contact@patrykkopycinski.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Christos Nasikas 2020-02-20 00:16:33 +02:00 committed by GitHub
parent 89f24ad30e
commit 19f7c20df0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 546 additions and 632 deletions

View file

@ -0,0 +1,26 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`EditableTitle it renders 1`] = `
<EuiFlexGroup
alignItems="center"
gutterSize="none"
>
<EuiFlexItem
grow={false}
>
<Memo(TitleComponent)
title="Test title"
/>
</EuiFlexItem>
<EuiFlexItem
grow={false}
>
<StyledEuiButtonIcon
aria-label="You can edit Test title by clicking"
data-test-subj="editable-title-edit-icon"
iconType="pencil"
onClick={[Function]}
/>
</EuiFlexItem>
</EuiFlexGroup>
`;

View file

@ -8,21 +8,16 @@ exports[`HeaderPage it renders 1`] = `
alignItems="center"
>
<FlexItem>
<EuiTitle
size="l"
>
<h1
data-test-subj="header-page-title"
>
Test title
<StyledEuiBetaBadge
label="Beta"
tooltipContent="Test tooltip"
tooltipPosition="bottom"
/>
</h1>
</EuiTitle>
<Memo(TitleComponent)
badgeOptions={
Object {
"beta": true,
"text": "Beta",
"tooltip": "Test tooltip",
}
}
title="Test title"
/>
<Subtitle
data-test-subj="header-page-subtitle"
items="Test subtitle"

View file

@ -0,0 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Title it renders 1`] = `
<EuiTitle
size="l"
>
<h1
data-test-subj="header-page-title"
>
Test title
<StyledEuiBetaBadge
label="Beta"
tooltipContent="Test tooltip"
tooltipPosition="bottom"
/>
</h1>
</EuiTitle>
`;

View file

@ -0,0 +1,192 @@
/*
* 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 { shallow } from 'enzyme';
import React from 'react';
import { TestProviders } from '../../mock';
import { EditableTitle } from './editable_title';
import { useMountAppended } from '../../utils/use_mount_appended';
describe('EditableTitle', () => {
const mount = useMountAppended();
const submitTitle = jest.fn();
test('it renders', () => {
const wrapper = shallow(
<EditableTitle title="Test title" onSubmit={submitTitle} isLoading={false} />
);
expect(wrapper).toMatchSnapshot();
});
test('it shows the edit title input field', () => {
const wrapper = mount(
<TestProviders>
<EditableTitle title="Test title" onSubmit={submitTitle} isLoading={false} />
</TestProviders>
);
wrapper.find('button[data-test-subj="editable-title-edit-icon"]').simulate('click');
wrapper.update();
expect(
wrapper
.find('[data-test-subj="editable-title-input-field"]')
.first()
.exists()
).toBe(true);
});
test('it shows the submit button', () => {
const wrapper = mount(
<TestProviders>
<EditableTitle title="Test title" onSubmit={submitTitle} isLoading={false} />
</TestProviders>
);
wrapper.find('button[data-test-subj="editable-title-edit-icon"]').simulate('click');
wrapper.update();
expect(
wrapper
.find('[data-test-subj="editable-title-submit-btn"]')
.first()
.exists()
).toBe(true);
});
test('it shows the cancel button', () => {
const wrapper = mount(
<TestProviders>
<EditableTitle title="Test title" onSubmit={submitTitle} isLoading={false} />
</TestProviders>
);
wrapper.find('button[data-test-subj="editable-title-edit-icon"]').simulate('click');
wrapper.update();
expect(
wrapper
.find('[data-test-subj="editable-title-cancel-btn"]')
.first()
.exists()
).toBe(true);
});
test('it DOES NOT shows the edit icon when in edit mode', () => {
const wrapper = mount(
<TestProviders>
<EditableTitle title="Test title" onSubmit={submitTitle} isLoading={false} />
</TestProviders>
);
wrapper.find('button[data-test-subj="editable-title-edit-icon"]').simulate('click');
wrapper.update();
expect(
wrapper
.find('[data-test-subj="editable-title-edit-icon"]')
.first()
.exists()
).toBe(false);
});
test('it switch to non edit mode when canceled', () => {
const wrapper = mount(
<TestProviders>
<EditableTitle title="Test title" onSubmit={submitTitle} isLoading={false} />
</TestProviders>
);
wrapper.find('button[data-test-subj="editable-title-edit-icon"]').simulate('click');
wrapper.update();
wrapper.find('button[data-test-subj="editable-title-cancel-btn"]').simulate('click');
expect(
wrapper
.find('[data-test-subj="editable-title-edit-icon"]')
.first()
.exists()
).toBe(true);
});
test('it should change the title', () => {
const newTitle = 'new test title';
const wrapper = mount(
<TestProviders>
<EditableTitle title="Test title" onSubmit={submitTitle} isLoading={false} />
</TestProviders>
);
wrapper.find('button[data-test-subj="editable-title-edit-icon"]').simulate('click');
wrapper.update();
wrapper
.find('input[data-test-subj="editable-title-input-field"]')
.simulate('change', { target: { value: newTitle } });
wrapper.update();
expect(
wrapper.find('input[data-test-subj="editable-title-input-field"]').prop('value')
).toEqual(newTitle);
});
test('it should NOT change the title when cancel', () => {
const title = 'Test title';
const newTitle = 'new test title';
const wrapper = mount(
<TestProviders>
<EditableTitle title={title} onSubmit={submitTitle} isLoading={false} />
</TestProviders>
);
wrapper.find('button[data-test-subj="editable-title-edit-icon"]').simulate('click');
wrapper.update();
wrapper
.find('input[data-test-subj="editable-title-input-field"]')
.simulate('change', { target: { value: newTitle } });
wrapper.update();
wrapper.find('button[data-test-subj="editable-title-cancel-btn"]').simulate('click');
wrapper.update();
expect(wrapper.find('h1[data-test-subj="header-page-title"]').text()).toEqual(title);
});
test('it submits the title', () => {
const newTitle = 'new test title';
const wrapper = mount(
<TestProviders>
<EditableTitle title="Test title" onSubmit={submitTitle} isLoading={false} />
</TestProviders>
);
wrapper.find('button[data-test-subj="editable-title-edit-icon"]').simulate('click');
wrapper.update();
wrapper
.find('input[data-test-subj="editable-title-input-field"]')
.simulate('change', { target: { value: newTitle } });
wrapper.find('button[data-test-subj="editable-title-submit-btn"]').simulate('click');
wrapper.update();
expect(submitTitle).toHaveBeenCalled();
expect(submitTitle.mock.calls[0][0]).toEqual(newTitle);
expect(
wrapper
.find('[data-test-subj="editable-title-edit-icon"]')
.first()
.exists()
).toBe(true);
});
});

View file

@ -0,0 +1,98 @@
/*
* 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, useCallback } from 'react';
import styled, { css } from 'styled-components';
import {
EuiButton,
EuiButtonEmpty,
EuiFlexGroup,
EuiFlexItem,
EuiFieldText,
EuiButtonIcon,
} from '@elastic/eui';
import * as i18n from './translations';
import { Title } from './title';
const StyledEuiButtonIcon = styled(EuiButtonIcon)`
${({ theme }) => css`
margin-left: ${theme.eui.euiSize};
`}
`;
StyledEuiButtonIcon.displayName = 'StyledEuiButtonIcon';
interface Props {
isLoading: boolean;
title: string | React.ReactNode;
onSubmit: (title: string) => void;
}
const EditableTitleComponent: React.FC<Props> = ({ onSubmit, isLoading, title }) => {
const [editMode, setEditMode] = useState(false);
const [changedTitle, onTitleChange] = useState(title);
const onCancel = useCallback(() => setEditMode(false), []);
const onClickEditIcon = useCallback(() => setEditMode(true), []);
const onClickSubmit = useCallback(
(newTitle: string): void => {
onSubmit(newTitle);
setEditMode(false);
},
[changedTitle]
);
return editMode ? (
<EuiFlexGroup alignItems="center" gutterSize="m" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiFieldText
onChange={e => onTitleChange(e.target.value)}
value={`${changedTitle}`}
data-test-subj="editable-title-input-field"
/>
</EuiFlexItem>
<EuiFlexGroup gutterSize="none" responsive={false} wrap={true}>
<EuiFlexItem grow={false}>
<EuiButton
fill
isDisabled={isLoading}
isLoading={isLoading}
onClick={() => onClickSubmit(changedTitle as string)}
data-test-subj="editable-title-submit-btn"
>
{i18n.SUBMIT}
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty onClick={onCancel} data-test-subj="editable-title-cancel-btn">
{i18n.CANCEL}
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexItem />
</EuiFlexGroup>
) : (
<EuiFlexGroup alignItems="center" gutterSize="none">
<EuiFlexItem grow={false}>
<Title title={title} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<StyledEuiButtonIcon
aria-label={i18n.EDIT_TITLE_ARIA(title as string)}
iconType="pencil"
onClick={onClickEditIcon}
data-test-subj="editable-title-edit-icon"
/>
</EuiFlexItem>
</EuiFlexGroup>
);
};
export const EditableTitle = React.memo(EditableTitleComponent);

View file

@ -31,21 +31,6 @@ describe('HeaderPage', () => {
expect(wrapper).toMatchSnapshot();
});
test('it renders the title', () => {
const wrapper = mount(
<TestProviders>
<HeaderPage title="Test title" />
</TestProviders>
);
expect(
wrapper
.find('[data-test-subj="header-page-title"]')
.first()
.exists()
).toBe(true);
});
test('it renders the back link when provided', () => {
const wrapper = mount(
<TestProviders>
@ -191,34 +176,4 @@ describe('HeaderPage', () => {
expect(siemHeaderPage).not.toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin);
expect(siemHeaderPage).not.toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.l);
});
test('it renders as a draggable when arguments provided', () => {
const wrapper = mount(
<TestProviders>
<HeaderPage draggableArguments={{ field: 'neat', value: 'cool' }} title="Test title" />
</TestProviders>
);
expect(
wrapper
.find('[data-test-subj="header-page-draggable"]')
.first()
.exists()
).toBe(true);
});
test('it DOES NOT render as a draggable when arguments not provided', () => {
const wrapper = mount(
<TestProviders>
<HeaderPage title="Test title" />
</TestProviders>
);
expect(
wrapper
.find('[data-test-subj="header-page-draggable"]')
.first()
.exists()
).toBe(false);
});
});

View file

@ -4,20 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
import {
EuiBetaBadge,
EuiBadge,
EuiFlexGroup,
EuiFlexItem,
EuiProgress,
EuiTitle,
} from '@elastic/eui';
import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiProgress } from '@elastic/eui';
import React from 'react';
import styled, { css } from 'styled-components';
import { DefaultDraggable } from '../draggables';
import { LinkIcon, LinkIconProps } from '../link_icon';
import { Subtitle, SubtitleProps } from '../subtitle';
import { Title } from './title';
import { DraggableArguments, BadgeOptions, TitleProp } from './types';
interface HeaderProps {
border?: boolean;
@ -63,28 +57,11 @@ const Badge = styled(EuiBadge)`
`;
Badge.displayName = 'Badge';
const StyledEuiBetaBadge = styled(EuiBetaBadge)`
vertical-align: middle;
`;
StyledEuiBetaBadge.displayName = 'StyledEuiBetaBadge';
interface BackOptions {
href: LinkIconProps['href'];
text: LinkIconProps['children'];
}
interface BadgeOptions {
beta?: boolean;
text: string;
tooltip?: string;
}
interface DraggableArguments {
field: string;
value: string;
}
export interface HeaderPageProps extends HeaderProps {
backOptions?: BackOptions;
badgeOptions?: BadgeOptions;
@ -92,7 +69,8 @@ export interface HeaderPageProps extends HeaderProps {
draggableArguments?: DraggableArguments;
subtitle?: SubtitleProps['items'];
subtitle2?: SubtitleProps['items'];
title: string | React.ReactNode;
title: TitleProp;
titleNode?: React.ReactElement;
}
const HeaderPageComponent: React.FC<HeaderPageProps> = ({
@ -105,6 +83,7 @@ const HeaderPageComponent: React.FC<HeaderPageProps> = ({
subtitle,
subtitle2,
title,
titleNode,
...rest
}) => (
<Header border={border} {...rest}>
@ -118,34 +97,13 @@ const HeaderPageComponent: React.FC<HeaderPageProps> = ({
</LinkBack>
)}
<EuiTitle size="l">
<h1 data-test-subj="header-page-title">
{!draggableArguments ? (
title
) : (
<DefaultDraggable
data-test-subj="header-page-draggable"
id={`header-page-draggable-${draggableArguments.field}-${draggableArguments.value}`}
field={draggableArguments.field}
value={`${draggableArguments.value}`}
/>
)}
{badgeOptions && (
<>
{' '}
{badgeOptions.beta ? (
<StyledEuiBetaBadge
label={badgeOptions.text}
tooltipContent={badgeOptions.tooltip}
tooltipPosition="bottom"
/>
) : (
<Badge color="hollow">{badgeOptions.text}</Badge>
)}
</>
)}
</h1>
</EuiTitle>
{titleNode || (
<Title
draggableArguments={draggableArguments}
title={title}
badgeOptions={badgeOptions}
/>
)}
{subtitle && <Subtitle data-test-subj="header-page-subtitle" items={subtitle} />}
{subtitle2 && <Subtitle data-test-subj="header-page-subtitle-2" items={subtitle2} />}

View file

@ -0,0 +1,72 @@
/*
* 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 { shallow } from 'enzyme';
import React from 'react';
import { TestProviders } from '../../mock';
import { Title } from './title';
import { useMountAppended } from '../../utils/use_mount_appended';
describe('Title', () => {
const mount = useMountAppended();
test('it renders', () => {
const wrapper = shallow(
<Title
badgeOptions={{ beta: true, text: 'Beta', tooltip: 'Test tooltip' }}
title="Test title"
/>
);
expect(wrapper).toMatchSnapshot();
});
test('it renders the title', () => {
const wrapper = mount(
<TestProviders>
<Title title="Test title" />
</TestProviders>
);
expect(
wrapper
.find('[data-test-subj="header-page-title"]')
.first()
.exists()
).toBe(true);
});
test('it renders as a draggable when arguments provided', () => {
const wrapper = mount(
<TestProviders>
<Title draggableArguments={{ field: 'neat', value: 'cool' }} title="Test title" />
</TestProviders>
);
expect(
wrapper
.find('[data-test-subj="header-page-draggable"]')
.first()
.exists()
).toBe(true);
});
test('it DOES NOT render as a draggable when arguments not provided', () => {
const wrapper = mount(
<TestProviders>
<Title title="Test title" />
</TestProviders>
);
expect(
wrapper
.find('[data-test-subj="header-page-draggable"]')
.first()
.exists()
).toBe(false);
});
});

View file

@ -0,0 +1,62 @@
/*
* 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 from 'react';
import { EuiBetaBadge, EuiBadge, EuiTitle } from '@elastic/eui';
import styled from 'styled-components';
import { DraggableArguments, BadgeOptions, TitleProp } from './types';
import { DefaultDraggable } from '../draggables';
const StyledEuiBetaBadge = styled(EuiBetaBadge)`
vertical-align: middle;
`;
StyledEuiBetaBadge.displayName = 'StyledEuiBetaBadge';
const Badge = styled(EuiBadge)`
letter-spacing: 0;
`;
Badge.displayName = 'Badge';
interface Props {
badgeOptions?: BadgeOptions;
title: TitleProp;
draggableArguments?: DraggableArguments;
}
const TitleComponent: React.FC<Props> = ({ draggableArguments, title, badgeOptions }) => (
<EuiTitle size="l">
<h1 data-test-subj="header-page-title">
{!draggableArguments ? (
title
) : (
<DefaultDraggable
data-test-subj="header-page-draggable"
id={`header-page-draggable-${draggableArguments.field}-${draggableArguments.value}`}
field={draggableArguments.field}
value={`${draggableArguments.value}`}
/>
)}
{badgeOptions && (
<>
{' '}
{badgeOptions.beta ? (
<StyledEuiBetaBadge
label={badgeOptions.text}
tooltipContent={badgeOptions.tooltip}
tooltipPosition="bottom"
/>
) : (
<Badge color="hollow">{badgeOptions.text}</Badge>
)}
</>
)}
</h1>
</EuiTitle>
);
export const Title = React.memo(TitleComponent);

View file

@ -0,0 +1,21 @@
/*
* 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 { i18n } from '@kbn/i18n';
export const SUBMIT = i18n.translate('xpack.siem.header.editableTitle.submit', {
defaultMessage: 'Submit',
});
export const CANCEL = i18n.translate('xpack.siem.header.editableTitle.cancel', {
defaultMessage: 'Cancel',
});
export const EDIT_TITLE_ARIA = (title: string) =>
i18n.translate('xpack.siem.header.editableTitle.editButtonAria', {
values: { title },
defaultMessage: 'You can edit {title} by clicking',
});

View file

@ -0,0 +1,18 @@
/*
* 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.
*/
export type TitleProp = string | React.ReactNode;
export interface DraggableArguments {
field: string;
value: string;
}
export interface BadgeOptions {
beta?: boolean;
text: string;
tooltip?: string;
}

View file

@ -1,47 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`HeaderPage it renders 1`] = `
<Header
border={true}
>
<EuiFlexGroup
alignItems="center"
justifyContent="spaceBetween"
>
<FlexItem
grow={false}
>
<EuiTitle
size="l"
>
<h1
data-test-subj="header-page-title"
>
Test title
<StyledEuiBetaBadge
label="Beta"
tooltipContent="Test tooltip"
tooltipPosition="bottom"
/>
</h1>
</EuiTitle>
<Subtitle
data-test-subj="header-page-subtitle"
items="Test subtitle"
/>
<Subtitle
data-test-subj="header-page-subtitle-2"
items="Test subtitle 2"
/>
</FlexItem>
<FlexItem
data-test-subj="header-page-supplements"
>
<p>
Test supplement
</p>
</FlexItem>
</EuiFlexGroup>
</Header>
`;

View file

@ -1,224 +0,0 @@
/*
* 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 euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
import { shallow } from 'enzyme';
import React from 'react';
import { TestProviders } from '../../mock';
import { HeaderPage } from './index';
import { useMountAppended } from '../../utils/use_mount_appended';
describe('HeaderPage', () => {
const mount = useMountAppended();
test('it renders', () => {
const wrapper = shallow(
<HeaderPage
badgeOptions={{ beta: true, text: 'Beta', tooltip: 'Test tooltip' }}
border
subtitle="Test subtitle"
subtitle2="Test subtitle 2"
title="Test title"
>
<p>{'Test supplement'}</p>
</HeaderPage>
);
expect(wrapper).toMatchSnapshot();
});
test('it renders the title', () => {
const wrapper = mount(
<TestProviders>
<HeaderPage title="Test title" />
</TestProviders>
);
expect(
wrapper
.find('[data-test-subj="header-page-title"]')
.first()
.exists()
).toBe(true);
});
test('it renders the back link when provided', () => {
const wrapper = mount(
<TestProviders>
<HeaderPage backOptions={{ href: '#', text: 'Test link' }} title="Test title" />
</TestProviders>
);
expect(
wrapper
.find('.siemHeaderPage__linkBack')
.first()
.exists()
).toBe(true);
});
test('it DOES NOT render the back link when not provided', () => {
const wrapper = mount(
<TestProviders>
<HeaderPage title="Test title" />
</TestProviders>
);
expect(
wrapper
.find('.siemHeaderPage__linkBack')
.first()
.exists()
).toBe(false);
});
test('it renders the first subtitle when provided', () => {
const wrapper = mount(
<TestProviders>
<HeaderPage subtitle="Test subtitle" title="Test title" />
</TestProviders>
);
expect(
wrapper
.find('[data-test-subj="header-page-subtitle"]')
.first()
.exists()
).toBe(true);
});
test('it DOES NOT render the first subtitle when not provided', () => {
const wrapper = mount(
<TestProviders>
<HeaderPage title="Test title" />
</TestProviders>
);
expect(
wrapper
.find('[data-test-subj="header-section-subtitle"]')
.first()
.exists()
).toBe(false);
});
test('it renders the second subtitle when provided', () => {
const wrapper = mount(
<TestProviders>
<HeaderPage subtitle2="Test subtitle 2" title="Test title" />
</TestProviders>
);
expect(
wrapper
.find('[data-test-subj="header-page-subtitle-2"]')
.first()
.exists()
).toBe(true);
});
test('it DOES NOT render the second subtitle when not provided', () => {
const wrapper = mount(
<TestProviders>
<HeaderPage title="Test title" />
</TestProviders>
);
expect(
wrapper
.find('[data-test-subj="header-section-subtitle-2"]')
.first()
.exists()
).toBe(false);
});
test('it renders supplements when children provided', () => {
const wrapper = mount(
<TestProviders>
<HeaderPage title="Test title">
<p>{'Test supplement'}</p>
</HeaderPage>
</TestProviders>
);
expect(
wrapper
.find('[data-test-subj="header-page-supplements"]')
.first()
.exists()
).toBe(true);
});
test('it DOES NOT render supplements when children not provided', () => {
const wrapper = mount(
<TestProviders>
<HeaderPage title="Test title" />
</TestProviders>
);
expect(
wrapper
.find('[data-test-subj="header-page-supplements"]')
.first()
.exists()
).toBe(false);
});
test('it applies border styles when border is true', () => {
const wrapper = mount(
<TestProviders>
<HeaderPage border title="Test title" />
</TestProviders>
);
const siemHeaderPage = wrapper.find('.siemHeaderPage').first();
expect(siemHeaderPage).toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin);
expect(siemHeaderPage).toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.l);
});
test('it DOES NOT apply border styles when border is false', () => {
const wrapper = mount(
<TestProviders>
<HeaderPage title="Test title" />
</TestProviders>
);
const siemHeaderPage = wrapper.find('.siemHeaderPage').first();
expect(siemHeaderPage).not.toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin);
expect(siemHeaderPage).not.toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.l);
});
test('it renders as a draggable when arguments provided', () => {
const wrapper = mount(
<TestProviders>
<HeaderPage draggableArguments={{ field: 'neat', value: 'cool' }} title="Test title" />
</TestProviders>
);
expect(
wrapper
.find('[data-test-subj="header-page-draggable"]')
.first()
.exists()
).toBe(true);
});
test('it DOES NOT render as a draggable when arguments not provided', () => {
const wrapper = mount(
<TestProviders>
<HeaderPage title="Test title" />
</TestProviders>
);
expect(
wrapper
.find('[data-test-subj="header-page-draggable"]')
.first()
.exists()
).toBe(false);
});
});

View file

@ -1,220 +0,0 @@
/*
* 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 {
EuiBadge,
EuiBetaBadge,
EuiButton,
EuiButtonEmpty,
EuiButtonIcon,
EuiFieldText,
EuiFlexGroup,
EuiFlexItem,
EuiProgress,
EuiTitle,
} from '@elastic/eui';
import React from 'react';
import styled, { css } from 'styled-components';
import { DefaultDraggable } from '../draggables';
import { LinkIcon, LinkIconProps } from '../link_icon';
import { Subtitle, SubtitleProps } from '../subtitle';
import * as i18n from './translations';
interface HeaderProps {
border?: boolean;
isLoading?: boolean;
}
const Header = styled.header.attrs({
className: 'siemHeaderPage',
})<HeaderProps>`
${({ border, theme }) => css`
margin-bottom: ${theme.eui.euiSizeL};
${border &&
css`
border-bottom: ${theme.eui.euiBorderThin};
padding-bottom: ${theme.eui.paddingSizes.l};
.euiProgress {
top: ${theme.eui.paddingSizes.l};
}
`}
`}
`;
Header.displayName = 'Header';
const FlexItem = styled(EuiFlexItem)`
display: block;
`;
FlexItem.displayName = 'FlexItem';
const LinkBack = styled.div.attrs({
className: 'siemHeaderPage__linkBack',
})`
${({ theme }) => css`
font-size: ${theme.eui.euiFontSizeXS};
line-height: ${theme.eui.euiLineHeight};
margin-bottom: ${theme.eui.euiSizeS};
`}
`;
LinkBack.displayName = 'LinkBack';
const Badge = styled(EuiBadge)`
letter-spacing: 0;
`;
Badge.displayName = 'Badge';
const StyledEuiBetaBadge = styled(EuiBetaBadge)`
vertical-align: middle;
`;
StyledEuiBetaBadge.displayName = 'StyledEuiBetaBadge';
const StyledEuiButtonIcon = styled(EuiButtonIcon)`
${({ theme }) => css`
margin-left: ${theme.eui.euiSize};
`}
`;
StyledEuiButtonIcon.displayName = 'StyledEuiButtonIcon';
interface BackOptions {
href: LinkIconProps['href'];
text: LinkIconProps['children'];
}
interface BadgeOptions {
beta?: boolean;
text: string;
tooltip?: string;
}
interface DraggableArguments {
field: string;
value: string;
}
interface IconAction {
'aria-label': string;
iconType: string;
onChange: (a: string) => void;
onClick: (b: boolean) => void;
onSubmit: () => void;
}
export interface HeaderPageProps extends HeaderProps {
backOptions?: BackOptions;
badgeOptions?: BadgeOptions;
children?: React.ReactNode;
draggableArguments?: DraggableArguments;
isEditTitle?: boolean;
iconAction?: IconAction;
subtitle2?: SubtitleProps['items'];
subtitle?: SubtitleProps['items'];
title: string | React.ReactNode;
}
const HeaderPageComponent: React.FC<HeaderPageProps> = ({
backOptions,
badgeOptions,
border,
children,
draggableArguments,
isEditTitle,
iconAction,
isLoading,
subtitle,
subtitle2,
title,
...rest
}) => (
<Header border={border} {...rest}>
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
<FlexItem grow={false}>
{backOptions && (
<LinkBack>
<LinkIcon href={backOptions.href} iconType="arrowLeft">
{backOptions.text}
</LinkIcon>
</LinkBack>
)}
{isEditTitle && iconAction ? (
<EuiFlexGroup alignItems="center" gutterSize="m" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiFieldText
onChange={e => iconAction.onChange(e.target.value)}
value={`${title}`}
/>
</EuiFlexItem>
<EuiFlexGroup gutterSize="none" responsive={false} wrap={true}>
<EuiFlexItem grow={false}>
<EuiButton
fill
isDisabled={isLoading}
isLoading={isLoading}
onClick={iconAction.onSubmit}
>
{i18n.SUBMIT}
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty onClick={() => iconAction.onClick(false)}>
{i18n.CANCEL}
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexItem />
</EuiFlexGroup>
) : (
<EuiTitle size="l">
<h1 data-test-subj="header-page-title">
{!draggableArguments ? (
title
) : (
<DefaultDraggable
data-test-subj="header-page-draggable"
id={`header-page-draggable-${draggableArguments.field}-${draggableArguments.value}`}
field={draggableArguments.field}
value={`${draggableArguments.value}`}
/>
)}
{badgeOptions && (
<>
{' '}
{badgeOptions.beta ? (
<StyledEuiBetaBadge
label={badgeOptions.text}
tooltipContent={badgeOptions.tooltip}
tooltipPosition="bottom"
/>
) : (
<Badge color="hollow">{badgeOptions.text}</Badge>
)}
</>
)}
{iconAction && (
<StyledEuiButtonIcon
aria-label={iconAction['aria-label']}
iconType={iconAction.iconType}
onClick={() => iconAction.onClick(true)}
/>
)}
</h1>
</EuiTitle>
)}
{subtitle && <Subtitle data-test-subj="header-page-subtitle" items={subtitle} />}
{subtitle2 && <Subtitle data-test-subj="header-page-subtitle-2" items={subtitle2} />}
{border && isLoading && <EuiProgress size="xs" color="accent" />}
</FlexItem>
{children && <FlexItem data-test-subj="header-page-supplements">{children}</FlexItem>}
</EuiFlexGroup>
</Header>
);
export const HeaderPage = React.memo(HeaderPageComponent);

View file

@ -1,15 +0,0 @@
/*
* 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 { i18n } from '@kbn/i18n';
export const SUBMIT = i18n.translate('xpack.siem.case.casePage.title.submit', {
defaultMessage: 'Submit',
});
export const CANCEL = i18n.translate('xpack.siem.case.casePage.title.cancel', {
defaultMessage: 'Cancel',
});

View file

@ -24,7 +24,8 @@ import { DescriptionMarkdown } from '../description_md_editor';
import { Case } from '../../../../containers/case/types';
import { FormattedRelativePreferenceDate } from '../../../../components/formatted_date';
import { getCaseUrl } from '../../../../components/link_to';
import { HeaderPage } from '../../../../components/header_page_new';
import { HeaderPage } from '../../../../components/header_page';
import { EditableTitle } from '../../../../components/header_page/editable_title';
import { Markdown } from '../../../../components/markdown';
import { PropertyActions } from '../property_actions';
import { TagList } from '../tag_list';
@ -50,6 +51,7 @@ const MyDescriptionList = styled(EuiDescriptionList)`
const MyWrapper = styled(WrapperPage)`
padding-bottom: 0;
`;
const BackgroundWrapper = styled.div`
${({ theme }) => css`
background-color: ${theme.eui.euiColorEmptyShade};
@ -67,7 +69,6 @@ interface CasesProps {
export const Cases = React.memo<CasesProps>(({ caseId, initialData, isLoading }) => {
const [{ data }, dispatchUpdateCaseProperty] = useUpdateCase(caseId, initialData);
const [isEditDescription, setIsEditDescription] = useState(false);
const [isEditTitle, setIsEditTitle] = useState(false);
const [isEditTags, setIsEditTags] = useState(false);
const [isCaseOpen, setIsCaseOpen] = useState(data.state === 'open');
const [description, setDescription] = useState(data.description);
@ -83,7 +84,6 @@ export const Cases = React.memo<CasesProps>(({ caseId, initialData, isLoading })
updateKey: 'title',
updateValue,
});
setIsEditTitle(false);
}
break;
case 'description':
@ -210,6 +210,17 @@ export const Cases = React.memo<CasesProps>(({ caseId, initialData, isLoading })
),
},
];
const onSubmit = useCallback(
newTitle => {
onUpdateField('title', newTitle);
setTitle(newTitle);
},
[title]
);
const titleNode = <EditableTitle isLoading={isLoading} title={title} onSubmit={onSubmit} />;
return (
<>
<MyWrapper>
@ -218,14 +229,7 @@ export const Cases = React.memo<CasesProps>(({ caseId, initialData, isLoading })
href: getCaseUrl(),
text: i18n.BACK_TO_ALL,
}}
iconAction={{
'aria-label': title,
iconType: 'pencil',
onChange: newTitle => setTitle(newTitle),
onSubmit: () => onUpdateField('title', title),
onClick: isEdit => setIsEditTitle(isEdit),
}}
isEditTitle={isEditTitle}
titleNode={titleNode}
title={title}
>
<EuiFlexGroup gutterSize="l" justifyContent="flexEnd">