mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
[Cases] Attachment framework improvements (#154450)
## Summary This PR adds the ability for consumers of the attachment framework to a) define their own label for the removed attachment user action and b) register custom action either as primary or as property actions. ### Checklist Delete any items that are not applicable to this PR. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [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 ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
This commit is contained in:
parent
234f09b18d
commit
3f7df8636f
10 changed files with 624 additions and 266 deletions
|
@ -6,16 +6,24 @@
|
|||
*/
|
||||
|
||||
import type React from 'react';
|
||||
import type { EuiCommentProps, IconType } from '@elastic/eui';
|
||||
import type { EuiCommentProps, IconType, EuiButtonProps } from '@elastic/eui';
|
||||
import type {
|
||||
CommentRequestExternalReferenceType,
|
||||
CommentRequestPersistableStateType,
|
||||
} from '../../../common/api';
|
||||
import type { Case } from '../../containers/types';
|
||||
|
||||
export interface AttachmentAction {
|
||||
onClick: () => void;
|
||||
iconType: string;
|
||||
label: string;
|
||||
color?: EuiButtonProps['color'];
|
||||
isPrimary?: boolean;
|
||||
}
|
||||
|
||||
export interface AttachmentViewObject<Props = {}> {
|
||||
timelineAvatar?: EuiCommentProps['timelineAvatar'];
|
||||
actions?: EuiCommentProps['actions'];
|
||||
getActions?: (props: Props) => AttachmentAction[];
|
||||
event?: EuiCommentProps['event'];
|
||||
children?: React.LazyExoticComponent<React.FC<Props>>;
|
||||
}
|
||||
|
@ -39,6 +47,7 @@ export interface AttachmentType<Props> {
|
|||
icon: IconType;
|
||||
displayName: string;
|
||||
getAttachmentViewObject: () => AttachmentViewObject<Props>;
|
||||
getAttachmentRemovalObject?: (props: Props) => Pick<AttachmentViewObject<Props>, 'event'>;
|
||||
}
|
||||
|
||||
export type ExternalReferenceAttachmentType = AttachmentType<ExternalReferenceAttachmentViewProps>;
|
||||
|
|
|
@ -140,6 +140,69 @@ describe('createCommentUserActionBuilder', () => {
|
|||
expect(screen.getByText('removed attachment')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders correctly when deleting an external reference attachment with getAttachmentRemovalObject defined', async () => {
|
||||
const getAttachmentRemovalObject = jest.fn().mockReturnValue({
|
||||
event: 'removed my own attachment',
|
||||
});
|
||||
|
||||
const externalReferenceAttachmentTypeRegistry = new ExternalReferenceAttachmentTypeRegistry();
|
||||
const attachment = getExternalReferenceAttachment();
|
||||
externalReferenceAttachmentTypeRegistry.register({
|
||||
...attachment,
|
||||
getAttachmentRemovalObject,
|
||||
});
|
||||
|
||||
const userAction = getExternalReferenceUserAction({ action: Actions.delete });
|
||||
const builder = createCommentUserActionBuilder({
|
||||
...builderArgs,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
userAction,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [externalReferenceAttachment],
|
||||
},
|
||||
});
|
||||
|
||||
const createdUserAction = builder.build();
|
||||
render(
|
||||
<TestProviders>
|
||||
<EuiCommentList comments={createdUserAction} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByText('removed my own attachment')).toBeInTheDocument();
|
||||
expect(getAttachmentRemovalObject).toBeCalledWith({
|
||||
caseData: {
|
||||
id: 'basic-case-id',
|
||||
title: 'Another horrible breach!!',
|
||||
},
|
||||
externalReferenceId: 'my-id',
|
||||
externalReferenceMetadata: null,
|
||||
});
|
||||
});
|
||||
|
||||
it('renders correctly when deleting an external reference attachment without getAttachmentRemovalObject defined', async () => {
|
||||
const externalReferenceAttachmentTypeRegistry = new ExternalReferenceAttachmentTypeRegistry();
|
||||
const attachment = getExternalReferenceAttachment();
|
||||
externalReferenceAttachmentTypeRegistry.register(attachment);
|
||||
|
||||
const userAction = getExternalReferenceUserAction({ action: Actions.delete });
|
||||
const builder = createCommentUserActionBuilder({
|
||||
...builderArgs,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
userAction,
|
||||
});
|
||||
|
||||
const createdUserAction = builder.build();
|
||||
render(
|
||||
<TestProviders>
|
||||
<EuiCommentList comments={createdUserAction} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByText('removed attachment')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders correctly when deleting a persistable state attachment', async () => {
|
||||
const userAction = getPersistableStateUserAction({ action: Actions.delete });
|
||||
const builder = createCommentUserActionBuilder({
|
||||
|
@ -156,6 +219,71 @@ describe('createCommentUserActionBuilder', () => {
|
|||
|
||||
expect(screen.getByText('removed attachment')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders correctly when deleting a persistable state attachment with getAttachmentRemovalObject defined', async () => {
|
||||
const getAttachmentRemovalObject = jest.fn().mockReturnValue({
|
||||
event: 'removed my own attachment',
|
||||
});
|
||||
|
||||
const persistableStateAttachmentTypeRegistry = new PersistableStateAttachmentTypeRegistry();
|
||||
const attachment = getPersistableStateAttachment();
|
||||
persistableStateAttachmentTypeRegistry.register({
|
||||
...attachment,
|
||||
getAttachmentRemovalObject,
|
||||
});
|
||||
|
||||
const userAction = getPersistableStateUserAction({ action: Actions.delete });
|
||||
const builder = createCommentUserActionBuilder({
|
||||
...builderArgs,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
userAction,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [persistableStateAttachment],
|
||||
},
|
||||
});
|
||||
|
||||
const createdUserAction = builder.build();
|
||||
render(
|
||||
<TestProviders>
|
||||
<EuiCommentList comments={createdUserAction} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByText('removed my own attachment')).toBeInTheDocument();
|
||||
expect(getAttachmentRemovalObject).toBeCalledWith({
|
||||
caseData: {
|
||||
id: 'basic-case-id',
|
||||
title: 'Another horrible breach!!',
|
||||
},
|
||||
persistableStateAttachmentTypeId: '.test',
|
||||
persistableStateAttachmentState: {
|
||||
test_foo: 'foo',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('renders correctly when deleting a persistable state attachment without getAttachmentRemovalObject defined', async () => {
|
||||
const persistableStateAttachmentTypeRegistry = new PersistableStateAttachmentTypeRegistry();
|
||||
const attachment = getPersistableStateAttachment();
|
||||
persistableStateAttachmentTypeRegistry.register(attachment);
|
||||
|
||||
const userAction = getPersistableStateUserAction({ action: Actions.delete });
|
||||
const builder = createCommentUserActionBuilder({
|
||||
...builderArgs,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
userAction,
|
||||
});
|
||||
|
||||
const createdUserAction = builder.build();
|
||||
render(
|
||||
<TestProviders>
|
||||
<EuiCommentList comments={createdUserAction} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByText('removed attachment')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('user comments', () => {
|
||||
|
@ -196,7 +324,7 @@ describe('createCommentUserActionBuilder', () => {
|
|||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(result.getByText('Solve this fast!')).toBeInTheDocument();
|
||||
expect(screen.getByText('Solve this fast!')).toBeInTheDocument();
|
||||
|
||||
await deleteAttachment(result, 'trash', 'Delete');
|
||||
|
||||
|
@ -219,20 +347,20 @@ describe('createCommentUserActionBuilder', () => {
|
|||
});
|
||||
|
||||
const createdUserAction = builder.build();
|
||||
const result = render(
|
||||
render(
|
||||
<TestProviders>
|
||||
<EuiCommentList comments={createdUserAction} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(result.getByText('Solve this fast!')).toBeInTheDocument();
|
||||
expect(result.getByTestId('property-actions-user-action')).toBeInTheDocument();
|
||||
expect(screen.getByText('Solve this fast!')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('property-actions-user-action')).toBeInTheDocument();
|
||||
|
||||
userEvent.click(result.getByTestId('property-actions-user-action-ellipses'));
|
||||
userEvent.click(screen.getByTestId('property-actions-user-action-ellipses'));
|
||||
await waitForEuiPopoverOpen();
|
||||
|
||||
expect(result.queryByTestId('property-actions-user-action-pencil')).toBeInTheDocument();
|
||||
userEvent.click(result.getByTestId('property-actions-user-action-pencil'));
|
||||
expect(screen.queryByTestId('property-actions-user-action-pencil')).toBeInTheDocument();
|
||||
userEvent.click(screen.getByTestId('property-actions-user-action-pencil'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(builderArgs.handleManageMarkdownEditId).toHaveBeenCalledWith('basic-comment-id');
|
||||
|
@ -250,20 +378,20 @@ describe('createCommentUserActionBuilder', () => {
|
|||
});
|
||||
|
||||
const createdUserAction = builder.build();
|
||||
const result = render(
|
||||
render(
|
||||
<TestProviders>
|
||||
<EuiCommentList comments={createdUserAction} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(result.getByText('Solve this fast!')).toBeInTheDocument();
|
||||
expect(result.getByTestId('property-actions-user-action')).toBeInTheDocument();
|
||||
expect(screen.getByText('Solve this fast!')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('property-actions-user-action')).toBeInTheDocument();
|
||||
|
||||
userEvent.click(result.getByTestId('property-actions-user-action-ellipses'));
|
||||
userEvent.click(screen.getByTestId('property-actions-user-action-ellipses'));
|
||||
await waitForEuiPopoverOpen();
|
||||
|
||||
expect(result.queryByTestId('property-actions-user-action-quote')).toBeInTheDocument();
|
||||
userEvent.click(result.getByTestId('property-actions-user-action-quote'));
|
||||
expect(screen.queryByTestId('property-actions-user-action-quote')).toBeInTheDocument();
|
||||
userEvent.click(screen.getByTestId('property-actions-user-action-quote'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(builderArgs.handleManageQuote).toHaveBeenCalledWith('Solve this fast!');
|
||||
|
@ -342,14 +470,14 @@ describe('createCommentUserActionBuilder', () => {
|
|||
});
|
||||
|
||||
const createdUserAction = builder.build();
|
||||
const result = render(
|
||||
render(
|
||||
<TestProviders>
|
||||
<EuiCommentList comments={createdUserAction} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(result.getByTestId('comment-action-show-alert-alert-action-id')).toBeInTheDocument();
|
||||
userEvent.click(result.getByTestId('comment-action-show-alert-alert-action-id'));
|
||||
expect(screen.getByTestId('comment-action-show-alert-alert-action-id')).toBeInTheDocument();
|
||||
userEvent.click(screen.getByTestId('comment-action-show-alert-alert-action-id'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(builderArgs.onShowAlertDetails).toHaveBeenCalledWith('alert-id-1', 'alert-index-1');
|
||||
|
@ -482,76 +610,249 @@ describe('createCommentUserActionBuilder', () => {
|
|||
expect(screen.getByText('I just isolated the host!')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe('External references', () => {
|
||||
describe('Attachment framework', () => {
|
||||
let appMockRender: AppMockRenderer;
|
||||
|
||||
beforeEach(() => {
|
||||
appMockRender = createAppMockRenderer();
|
||||
});
|
||||
|
||||
it('renders correctly an external reference', async () => {
|
||||
const externalReferenceAttachmentTypeRegistry = new ExternalReferenceAttachmentTypeRegistry();
|
||||
externalReferenceAttachmentTypeRegistry.register(getExternalReferenceAttachment());
|
||||
describe('External references', () => {
|
||||
it('renders correctly an external reference', async () => {
|
||||
const externalReferenceAttachmentTypeRegistry =
|
||||
new ExternalReferenceAttachmentTypeRegistry();
|
||||
externalReferenceAttachmentTypeRegistry.register(getExternalReferenceAttachment());
|
||||
|
||||
const userAction = getExternalReferenceUserAction();
|
||||
const damagedRaccoon = userProfiles[0];
|
||||
const builder = createCommentUserActionBuilder({
|
||||
...builderArgs,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [
|
||||
{
|
||||
...externalReferenceAttachment,
|
||||
createdBy: {
|
||||
username: damagedRaccoon.user.username,
|
||||
fullName: damagedRaccoon.user.full_name,
|
||||
email: damagedRaccoon.user.email,
|
||||
const userAction = getExternalReferenceUserAction();
|
||||
const damagedRaccoon = userProfiles[0];
|
||||
const builder = createCommentUserActionBuilder({
|
||||
...builderArgs,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [
|
||||
{
|
||||
...externalReferenceAttachment,
|
||||
createdBy: {
|
||||
username: damagedRaccoon.user.username,
|
||||
fullName: damagedRaccoon.user.full_name,
|
||||
email: damagedRaccoon.user.email,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
userAction,
|
||||
],
|
||||
},
|
||||
userAction,
|
||||
});
|
||||
|
||||
const createdUserAction = builder.build();
|
||||
appMockRender.render(<EuiCommentList comments={createdUserAction} />);
|
||||
|
||||
expect(screen.getByTestId('comment-externalReference-.test')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('copy-link-external-reference-comment-id')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('case-user-profile-avatar-damaged_raccoon')).toBeInTheDocument();
|
||||
expect(screen.getByText('added a chart')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const createdUserAction = builder.build();
|
||||
const result = appMockRender.render(<EuiCommentList comments={createdUserAction} />);
|
||||
it('renders correctly if the reference is not registered', async () => {
|
||||
const externalReferenceAttachmentTypeRegistry =
|
||||
new ExternalReferenceAttachmentTypeRegistry();
|
||||
|
||||
expect(result.getByTestId('comment-externalReference-.test')).toBeInTheDocument();
|
||||
expect(result.getByTestId('copy-link-external-reference-comment-id')).toBeInTheDocument();
|
||||
expect(result.getByTestId('case-user-profile-avatar-damaged_raccoon')).toBeInTheDocument();
|
||||
expect(screen.getByText('added a chart')).toBeInTheDocument();
|
||||
});
|
||||
const userAction = getExternalReferenceUserAction();
|
||||
const builder = createCommentUserActionBuilder({
|
||||
...builderArgs,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [externalReferenceAttachment],
|
||||
},
|
||||
userAction,
|
||||
});
|
||||
|
||||
it('renders correctly if the reference is not registered', async () => {
|
||||
const externalReferenceAttachmentTypeRegistry = new ExternalReferenceAttachmentTypeRegistry();
|
||||
const createdUserAction = builder.build();
|
||||
appMockRender.render(<EuiCommentList comments={createdUserAction} />);
|
||||
|
||||
const userAction = getExternalReferenceUserAction();
|
||||
const builder = createCommentUserActionBuilder({
|
||||
...builderArgs,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [externalReferenceAttachment],
|
||||
},
|
||||
userAction,
|
||||
expect(screen.getByTestId('comment-externalReference-not-found')).toBeInTheDocument();
|
||||
expect(screen.getByText('added an attachment of type')).toBeInTheDocument();
|
||||
expect(screen.getByText('Attachment type is not registered')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const createdUserAction = builder.build();
|
||||
const result = appMockRender.render(<EuiCommentList comments={createdUserAction} />);
|
||||
it('deletes the attachment correctly', async () => {
|
||||
const externalReferenceAttachmentTypeRegistry =
|
||||
new ExternalReferenceAttachmentTypeRegistry();
|
||||
externalReferenceAttachmentTypeRegistry.register(getExternalReferenceAttachment());
|
||||
|
||||
expect(result.getByTestId('comment-externalReference-not-found')).toBeInTheDocument();
|
||||
expect(screen.getByText('added an attachment of type')).toBeInTheDocument();
|
||||
expect(screen.getByText('Attachment type is not registered')).toBeInTheDocument();
|
||||
const userAction = getExternalReferenceUserAction();
|
||||
const builder = createCommentUserActionBuilder({
|
||||
...builderArgs,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [externalReferenceAttachment],
|
||||
},
|
||||
userAction,
|
||||
});
|
||||
|
||||
const createdUserAction = builder.build();
|
||||
const result = appMockRender.render(<EuiCommentList comments={createdUserAction} />);
|
||||
|
||||
expect(screen.getByTestId('comment-externalReference-.test')).toBeInTheDocument();
|
||||
|
||||
await deleteAttachment(result, 'trash', 'Delete');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(builderArgs.handleDeleteComment).toHaveBeenCalledWith(
|
||||
'external-reference-comment-id',
|
||||
'Deleted attachment'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('renders correctly an external reference with actions', async () => {
|
||||
const ActionsView = () => {
|
||||
return <>{'Attachment actions'}</>;
|
||||
};
|
||||
describe('Persistable state', () => {
|
||||
it('renders correctly a persistable state attachment', async () => {
|
||||
const MockComponent = jest.fn((props) => {
|
||||
return (
|
||||
<div data-test-subj={`attachment_${props.persistableStateAttachmentState.test_foo}`} />
|
||||
);
|
||||
});
|
||||
|
||||
const SpyLazyFactory = jest.fn(() => {
|
||||
return Promise.resolve().then(() => {
|
||||
return {
|
||||
default: React.memo(MockComponent),
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const persistableStateAttachmentTypeRegistry = new PersistableStateAttachmentTypeRegistry();
|
||||
persistableStateAttachmentTypeRegistry.register(
|
||||
getPersistableStateAttachment({
|
||||
children: React.lazy(SpyLazyFactory),
|
||||
})
|
||||
);
|
||||
|
||||
const userAction = getPersistableStateUserAction();
|
||||
const attachment01 = {
|
||||
...persistableStateAttachment,
|
||||
persistableStateAttachmentState: { test_foo: '01' },
|
||||
createdBy: {
|
||||
username: userProfiles[0].user.username,
|
||||
fullName: userProfiles[0].user.full_name,
|
||||
email: userProfiles[0].user.email,
|
||||
profileUid: userProfiles[0].uid,
|
||||
},
|
||||
};
|
||||
const builder = createCommentUserActionBuilder({
|
||||
...builderArgs,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [attachment01],
|
||||
},
|
||||
userAction,
|
||||
});
|
||||
|
||||
const result = appMockRender.render(<EuiCommentList comments={builder.build()} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('attachment_01')).toBeInTheDocument();
|
||||
expect(MockComponent).toHaveBeenCalledTimes(1);
|
||||
expect(SpyLazyFactory).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
expect(screen.getByTestId('comment-persistableState-.test')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('copy-link-persistable-state-comment-id')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('case-user-profile-avatar-damaged_raccoon')).toBeInTheDocument();
|
||||
expect(screen.getByText('added an embeddable')).toBeInTheDocument();
|
||||
|
||||
result.unmount();
|
||||
|
||||
const attachment02 = {
|
||||
...persistableStateAttachment,
|
||||
persistableStateAttachmentState: { test_foo: '02' },
|
||||
};
|
||||
const updateBuilder = createCommentUserActionBuilder({
|
||||
...builderArgs,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [attachment02],
|
||||
},
|
||||
userAction,
|
||||
});
|
||||
|
||||
const result2 = appMockRender.render(<EuiCommentList comments={updateBuilder.build()} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result2.getByTestId('attachment_02')).toBeInTheDocument();
|
||||
expect(MockComponent).toHaveBeenCalledTimes(2);
|
||||
expect(SpyLazyFactory).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders correctly if the reference is not registered', async () => {
|
||||
const persistableStateAttachmentTypeRegistry = new PersistableStateAttachmentTypeRegistry();
|
||||
|
||||
const userAction = getPersistableStateUserAction();
|
||||
const builder = createCommentUserActionBuilder({
|
||||
...builderArgs,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [persistableStateAttachment],
|
||||
},
|
||||
userAction,
|
||||
});
|
||||
|
||||
const createdUserAction = builder.build();
|
||||
appMockRender.render(<EuiCommentList comments={createdUserAction} />);
|
||||
|
||||
expect(screen.getByTestId('comment-persistableState-not-found')).toBeInTheDocument();
|
||||
expect(screen.getByText('added an attachment of type')).toBeInTheDocument();
|
||||
expect(screen.getByText('Attachment type is not registered')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('deletes the attachment correctly', async () => {
|
||||
const attachment = getPersistableStateAttachment();
|
||||
const persistableStateAttachmentTypeRegistry = new PersistableStateAttachmentTypeRegistry();
|
||||
persistableStateAttachmentTypeRegistry.register(attachment);
|
||||
|
||||
const userAction = getPersistableStateUserAction();
|
||||
const builder = createCommentUserActionBuilder({
|
||||
...builderArgs,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [persistableStateAttachment],
|
||||
},
|
||||
userAction,
|
||||
});
|
||||
|
||||
const createdUserAction = builder.build();
|
||||
const result = appMockRender.render(<EuiCommentList comments={createdUserAction} />);
|
||||
|
||||
expect(screen.getByTestId('comment-persistableState-.test')).toBeInTheDocument();
|
||||
|
||||
await deleteAttachment(result, 'trash', 'Delete');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(builderArgs.handleDeleteComment).toHaveBeenCalledWith(
|
||||
'persistable-state-comment-id',
|
||||
'Deleted attachment'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('shows correctly the visible primary actions', async () => {
|
||||
const onClick = jest.fn();
|
||||
|
||||
const attachment = getExternalReferenceAttachment({
|
||||
actions: <ActionsView />,
|
||||
getActions: () => [
|
||||
{ label: 'My primary button', isPrimary: true, iconType: 'danger', onClick },
|
||||
{ label: 'My primary 2 button', isPrimary: true, iconType: 'danger', onClick },
|
||||
{ label: 'My primary 3 button', isPrimary: true, iconType: 'danger', onClick },
|
||||
],
|
||||
});
|
||||
|
||||
const externalReferenceAttachmentTypeRegistry = new ExternalReferenceAttachmentTypeRegistry();
|
||||
|
@ -569,15 +870,37 @@ describe('createCommentUserActionBuilder', () => {
|
|||
});
|
||||
|
||||
const createdUserAction = builder.build();
|
||||
const result = appMockRender.render(<EuiCommentList comments={createdUserAction} />);
|
||||
appMockRender.render(<EuiCommentList comments={createdUserAction} />);
|
||||
|
||||
expect(result.getByTestId('comment-externalReference-.test')).toBeInTheDocument();
|
||||
expect(screen.getByText('Attachment actions')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('comment-externalReference-.test')).toBeInTheDocument();
|
||||
expect(screen.getByLabelText('My primary button')).toBeInTheDocument();
|
||||
expect(screen.getByLabelText('My primary 2 button')).toBeInTheDocument();
|
||||
expect(screen.queryByLabelText('My primary 3 button')).not.toBeInTheDocument();
|
||||
|
||||
userEvent.click(screen.getByLabelText('My primary button'), undefined, {
|
||||
skipPointerEventsCheck: true,
|
||||
});
|
||||
|
||||
userEvent.click(screen.getByLabelText('My primary 2 button'), undefined, {
|
||||
skipPointerEventsCheck: true,
|
||||
});
|
||||
|
||||
expect(onClick).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('deletes the attachment correctly', async () => {
|
||||
it('shows correctly the non visible primary actions', async () => {
|
||||
const onClick = jest.fn();
|
||||
|
||||
const attachment = getExternalReferenceAttachment({
|
||||
getActions: () => [
|
||||
{ label: 'My primary button', isPrimary: true, iconType: 'danger', onClick },
|
||||
{ label: 'My primary 2 button', isPrimary: true, iconType: 'danger', onClick },
|
||||
{ label: 'My primary 3 button', isPrimary: true, iconType: 'danger', onClick },
|
||||
],
|
||||
});
|
||||
|
||||
const externalReferenceAttachmentTypeRegistry = new ExternalReferenceAttachmentTypeRegistry();
|
||||
externalReferenceAttachmentTypeRegistry.register(getExternalReferenceAttachment());
|
||||
externalReferenceAttachmentTypeRegistry.register(attachment);
|
||||
|
||||
const userAction = getExternalReferenceUserAction();
|
||||
const builder = createCommentUserActionBuilder({
|
||||
|
@ -591,207 +914,128 @@ describe('createCommentUserActionBuilder', () => {
|
|||
});
|
||||
|
||||
const createdUserAction = builder.build();
|
||||
const result = appMockRender.render(<EuiCommentList comments={createdUserAction} />);
|
||||
appMockRender.render(<EuiCommentList comments={createdUserAction} />);
|
||||
|
||||
expect(result.getByTestId('comment-externalReference-.test')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('comment-externalReference-.test')).toBeInTheDocument();
|
||||
expect(screen.getByLabelText('My primary button')).toBeInTheDocument();
|
||||
expect(screen.getByLabelText('My primary 2 button')).toBeInTheDocument();
|
||||
expect(screen.queryByLabelText('My primary 3 button')).not.toBeInTheDocument();
|
||||
|
||||
await deleteAttachment(result, 'trash', 'Delete');
|
||||
expect(screen.getByTestId('property-actions-user-action')).toBeInTheDocument();
|
||||
userEvent.click(screen.getByTestId('property-actions-user-action-ellipses'));
|
||||
await waitForEuiPopoverOpen();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(builderArgs.handleDeleteComment).toHaveBeenCalledWith(
|
||||
'external-reference-comment-id',
|
||||
'Deleted attachment'
|
||||
);
|
||||
expect(screen.getByText('My primary 3 button')).toBeInTheDocument();
|
||||
|
||||
userEvent.click(screen.getByText('My primary 3 button'), undefined, {
|
||||
skipPointerEventsCheck: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Persistable state', () => {
|
||||
let appMockRender: AppMockRenderer;
|
||||
|
||||
beforeEach(() => {
|
||||
appMockRender = createAppMockRenderer();
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders correctly a persistable state attachment', async () => {
|
||||
const MockComponent = jest.fn((props) => {
|
||||
return (
|
||||
<div data-test-subj={`attachment_${props.persistableStateAttachmentState.test_foo}`} />
|
||||
);
|
||||
it('shows correctly the registered primary actions and non-primary actions', async () => {
|
||||
const onClick = jest.fn();
|
||||
|
||||
const attachment = getExternalReferenceAttachment({
|
||||
getActions: () => [
|
||||
{ label: 'My button', iconType: 'trash', onClick },
|
||||
{ label: 'My button 2', iconType: 'download', onClick },
|
||||
{ label: 'My primary button', isPrimary: true, iconType: 'danger', onClick },
|
||||
{ label: 'My primary 2 button', isPrimary: true, iconType: 'danger', onClick },
|
||||
{ label: 'My primary 3 button', isPrimary: true, iconType: 'danger', onClick },
|
||||
],
|
||||
});
|
||||
|
||||
const SpyLazyFactory = jest.fn(() => {
|
||||
return Promise.resolve().then(() => {
|
||||
return {
|
||||
default: React.memo(MockComponent),
|
||||
};
|
||||
});
|
||||
});
|
||||
const externalReferenceAttachmentTypeRegistry = new ExternalReferenceAttachmentTypeRegistry();
|
||||
externalReferenceAttachmentTypeRegistry.register(attachment);
|
||||
|
||||
const persistableStateAttachmentTypeRegistry = new PersistableStateAttachmentTypeRegistry();
|
||||
persistableStateAttachmentTypeRegistry.register(
|
||||
getPersistableStateAttachment({
|
||||
children: React.lazy(SpyLazyFactory),
|
||||
})
|
||||
);
|
||||
|
||||
const userAction = getPersistableStateUserAction();
|
||||
const attachment01 = {
|
||||
...persistableStateAttachment,
|
||||
persistableStateAttachmentState: { test_foo: '01' },
|
||||
createdBy: {
|
||||
username: userProfiles[0].user.username,
|
||||
fullName: userProfiles[0].user.full_name,
|
||||
email: userProfiles[0].user.email,
|
||||
profileUid: userProfiles[0].uid,
|
||||
},
|
||||
};
|
||||
const userAction = getExternalReferenceUserAction();
|
||||
const builder = createCommentUserActionBuilder({
|
||||
...builderArgs,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [attachment01],
|
||||
},
|
||||
userAction,
|
||||
});
|
||||
|
||||
const result = appMockRender.render(<EuiCommentList comments={builder.build()} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.getByTestId('attachment_01')).toBeInTheDocument();
|
||||
expect(MockComponent).toHaveBeenCalledTimes(1);
|
||||
expect(SpyLazyFactory).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
expect(result.getByTestId('comment-persistableState-.test')).toBeInTheDocument();
|
||||
expect(result.getByTestId('copy-link-persistable-state-comment-id')).toBeInTheDocument();
|
||||
expect(result.getByTestId('case-user-profile-avatar-damaged_raccoon')).toBeInTheDocument();
|
||||
expect(screen.getByText('added an embeddable')).toBeInTheDocument();
|
||||
|
||||
result.unmount();
|
||||
|
||||
const attachment02 = {
|
||||
...persistableStateAttachment,
|
||||
persistableStateAttachmentState: { test_foo: '02' },
|
||||
};
|
||||
const updateBuilder = createCommentUserActionBuilder({
|
||||
...builderArgs,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [attachment02],
|
||||
},
|
||||
userAction,
|
||||
});
|
||||
|
||||
const result2 = appMockRender.render(<EuiCommentList comments={updateBuilder.build()} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result2.getByTestId('attachment_02')).toBeInTheDocument();
|
||||
expect(MockComponent).toHaveBeenCalledTimes(2);
|
||||
expect(SpyLazyFactory).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders correctly if the reference is not registered', async () => {
|
||||
const persistableStateAttachmentTypeRegistry = new PersistableStateAttachmentTypeRegistry();
|
||||
|
||||
const userAction = getPersistableStateUserAction();
|
||||
const builder = createCommentUserActionBuilder({
|
||||
...builderArgs,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [persistableStateAttachment],
|
||||
comments: [externalReferenceAttachment],
|
||||
},
|
||||
userAction,
|
||||
});
|
||||
|
||||
const createdUserAction = builder.build();
|
||||
const result = appMockRender.render(<EuiCommentList comments={createdUserAction} />);
|
||||
appMockRender.render(<EuiCommentList comments={createdUserAction} />);
|
||||
|
||||
expect(result.getByTestId('comment-persistableState-not-found')).toBeInTheDocument();
|
||||
expect(screen.getByText('added an attachment of type')).toBeInTheDocument();
|
||||
expect(screen.getByText('Attachment type is not registered')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByTestId('comment-externalReference-.test')).toBeInTheDocument();
|
||||
expect(screen.getByLabelText('My primary button')).toBeInTheDocument();
|
||||
expect(screen.getByLabelText('My primary 2 button')).toBeInTheDocument();
|
||||
expect(screen.queryByLabelText('My primary 3 button')).not.toBeInTheDocument();
|
||||
|
||||
it('renders correctly a persistable state with actions', async () => {
|
||||
const ActionsView = () => {
|
||||
return <>{'Attachment actions'}</>;
|
||||
};
|
||||
expect(screen.getByTestId('property-actions-user-action')).toBeInTheDocument();
|
||||
userEvent.click(screen.getByTestId('property-actions-user-action-ellipses'));
|
||||
await waitForEuiPopoverOpen();
|
||||
|
||||
const attachment = getPersistableStateAttachment({
|
||||
actions: <ActionsView />,
|
||||
expect(screen.getByText('My button')).toBeInTheDocument();
|
||||
expect(screen.getByText('My button 2')).toBeInTheDocument();
|
||||
expect(screen.getByText('My primary 3 button')).toBeInTheDocument();
|
||||
|
||||
userEvent.click(screen.getByText('My button'), undefined, { skipPointerEventsCheck: true });
|
||||
userEvent.click(screen.getByText('My button 2'), undefined, {
|
||||
skipPointerEventsCheck: true,
|
||||
});
|
||||
|
||||
const persistableStateAttachmentTypeRegistry = new PersistableStateAttachmentTypeRegistry();
|
||||
persistableStateAttachmentTypeRegistry.register(attachment);
|
||||
expect(onClick).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
const userAction = getPersistableStateUserAction();
|
||||
it('divides correctly less than two primary actions', async () => {
|
||||
const onClick = jest.fn();
|
||||
|
||||
const attachment = getExternalReferenceAttachment({
|
||||
getActions: () => [
|
||||
{ label: 'My primary button', isPrimary: true, iconType: 'danger', onClick },
|
||||
],
|
||||
});
|
||||
|
||||
const externalReferenceAttachmentTypeRegistry = new ExternalReferenceAttachmentTypeRegistry();
|
||||
externalReferenceAttachmentTypeRegistry.register(attachment);
|
||||
|
||||
const userAction = getExternalReferenceUserAction();
|
||||
const builder = createCommentUserActionBuilder({
|
||||
...builderArgs,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [persistableStateAttachment],
|
||||
comments: [externalReferenceAttachment],
|
||||
},
|
||||
userAction,
|
||||
});
|
||||
|
||||
const createdUserAction = builder.build();
|
||||
const result = appMockRender.render(<EuiCommentList comments={createdUserAction} />);
|
||||
appMockRender.render(<EuiCommentList comments={createdUserAction} />);
|
||||
|
||||
expect(result.getByTestId('comment-persistableState-.test')).toBeInTheDocument();
|
||||
expect(screen.getByText('Attachment actions')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByTestId('comment-externalReference-.test')).toBeInTheDocument();
|
||||
expect(screen.getByLabelText('My primary button')).toBeInTheDocument();
|
||||
|
||||
it('deletes the attachment correctly', async () => {
|
||||
const attachment = getPersistableStateAttachment();
|
||||
const persistableStateAttachmentTypeRegistry = new PersistableStateAttachmentTypeRegistry();
|
||||
persistableStateAttachmentTypeRegistry.register(attachment);
|
||||
|
||||
const userAction = getPersistableStateUserAction();
|
||||
const builder = createCommentUserActionBuilder({
|
||||
...builderArgs,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [persistableStateAttachment],
|
||||
},
|
||||
userAction,
|
||||
userEvent.click(screen.getByLabelText('My primary button'), undefined, {
|
||||
skipPointerEventsCheck: true,
|
||||
});
|
||||
|
||||
const createdUserAction = builder.build();
|
||||
const result = appMockRender.render(<EuiCommentList comments={createdUserAction} />);
|
||||
|
||||
expect(result.getByTestId('comment-persistableState-.test')).toBeInTheDocument();
|
||||
|
||||
await deleteAttachment(result, 'trash', 'Delete');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(builderArgs.handleDeleteComment).toHaveBeenCalledWith(
|
||||
'persistable-state-comment-id',
|
||||
'Deleted attachment'
|
||||
);
|
||||
});
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const deleteAttachment = async (result: RenderResult, deleteIcon: string, buttonLabel: string) => {
|
||||
expect(result.getByTestId('property-actions-user-action')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('property-actions-user-action')).toBeInTheDocument();
|
||||
|
||||
userEvent.click(result.getByTestId('property-actions-user-action-ellipses'));
|
||||
userEvent.click(screen.getByTestId('property-actions-user-action-ellipses'));
|
||||
await waitForEuiPopoverOpen();
|
||||
|
||||
expect(result.queryByTestId(`property-actions-user-action-${deleteIcon}`)).toBeInTheDocument();
|
||||
expect(screen.queryByTestId(`property-actions-user-action-${deleteIcon}`)).toBeInTheDocument();
|
||||
|
||||
userEvent.click(result.getByTestId(`property-actions-user-action-${deleteIcon}`));
|
||||
userEvent.click(screen.getByTestId(`property-actions-user-action-${deleteIcon}`));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.queryByTestId('property-actions-confirm-modal')).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('property-actions-confirm-modal')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
userEvent.click(result.getByText(buttonLabel));
|
||||
userEvent.click(screen.getByText(buttonLabel));
|
||||
};
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import type { EuiCommentProps } from '@elastic/eui';
|
||||
|
||||
import type { AttachmentTypeRegistry } from '../../../../common/registry';
|
||||
import type { CommentUserAction } from '../../../../common/api';
|
||||
import { Actions, CommentType } from '../../../../common/api';
|
||||
import type { UserActionBuilder, UserActionBuilderArgs, UserActionResponse } from '../types';
|
||||
|
@ -18,9 +19,23 @@ import { createAlertAttachmentUserActionBuilder } from './alert';
|
|||
import { createActionAttachmentUserActionBuilder } from './actions';
|
||||
import { createExternalReferenceAttachmentUserActionBuilder } from './external_reference';
|
||||
import { createPersistableStateAttachmentUserActionBuilder } from './persistable_state';
|
||||
import type { AttachmentType } from '../../../client/attachment_framework/types';
|
||||
|
||||
const getUpdateLabelTitle = () => `${i18n.EDITED_FIELD} ${i18n.COMMENT.toLowerCase()}`;
|
||||
const getDeleteLabelTitle = (userAction: UserActionResponse<CommentUserAction>) => {
|
||||
|
||||
interface DeleteLabelTitle {
|
||||
userAction: UserActionResponse<CommentUserAction>;
|
||||
caseData: UserActionBuilderArgs['caseData'];
|
||||
externalReferenceAttachmentTypeRegistry: UserActionBuilderArgs['externalReferenceAttachmentTypeRegistry'];
|
||||
persistableStateAttachmentTypeRegistry: UserActionBuilderArgs['persistableStateAttachmentTypeRegistry'];
|
||||
}
|
||||
|
||||
const getDeleteLabelTitle = ({
|
||||
userAction,
|
||||
caseData,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
}: DeleteLabelTitle) => {
|
||||
const { comment } = userAction.payload;
|
||||
|
||||
if (comment.type === CommentType.alert) {
|
||||
|
@ -30,24 +45,90 @@ const getDeleteLabelTitle = (userAction: UserActionResponse<CommentUserAction>)
|
|||
return `${i18n.REMOVED_FIELD} ${alertLabel}`;
|
||||
}
|
||||
|
||||
if (
|
||||
comment.type === CommentType.externalReference ||
|
||||
comment.type === CommentType.persistableState
|
||||
) {
|
||||
return `${i18n.REMOVED_FIELD} ${i18n.ATTACHMENT.toLowerCase()}`;
|
||||
if (comment.type === CommentType.externalReference) {
|
||||
return getDeleteLabelFromRegistry({
|
||||
caseData,
|
||||
registry: externalReferenceAttachmentTypeRegistry,
|
||||
getId: () => comment.externalReferenceAttachmentTypeId,
|
||||
getAttachmentProps: () => ({
|
||||
externalReferenceId: comment.externalReferenceId,
|
||||
externalReferenceMetadata: comment.externalReferenceMetadata,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
if (comment.type === CommentType.persistableState) {
|
||||
return getDeleteLabelFromRegistry({
|
||||
caseData,
|
||||
registry: persistableStateAttachmentTypeRegistry,
|
||||
getId: () => comment.persistableStateAttachmentTypeId,
|
||||
getAttachmentProps: () => ({
|
||||
persistableStateAttachmentTypeId: comment.persistableStateAttachmentTypeId,
|
||||
persistableStateAttachmentState: comment.persistableStateAttachmentState,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
return `${i18n.REMOVED_FIELD} ${i18n.COMMENT.toLowerCase()}`;
|
||||
};
|
||||
|
||||
interface GetDeleteLabelFromRegistryArgs<R> {
|
||||
caseData: UserActionBuilderArgs['caseData'];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
registry: AttachmentTypeRegistry<AttachmentType<any>>;
|
||||
getId: () => string;
|
||||
getAttachmentProps: () => object;
|
||||
}
|
||||
|
||||
const getDeleteLabelFromRegistry = <R,>({
|
||||
caseData,
|
||||
registry,
|
||||
getId,
|
||||
getAttachmentProps,
|
||||
}: GetDeleteLabelFromRegistryArgs<R>) => {
|
||||
const registeredAttachmentCommonLabel = `${i18n.REMOVED_FIELD} ${i18n.ATTACHMENT.toLowerCase()}`;
|
||||
const attachmentTypeId: string = getId();
|
||||
const isTypeRegistered = registry.has(attachmentTypeId);
|
||||
|
||||
if (!isTypeRegistered) {
|
||||
return registeredAttachmentCommonLabel;
|
||||
}
|
||||
|
||||
const props = {
|
||||
...getAttachmentProps(),
|
||||
caseData: { id: caseData.id, title: caseData.title },
|
||||
};
|
||||
|
||||
const attachmentType = registry.get(attachmentTypeId);
|
||||
const attachmentLabel = attachmentType.getAttachmentRemovalObject?.(props).event ?? null;
|
||||
|
||||
return attachmentLabel != null ? attachmentLabel : registeredAttachmentCommonLabel;
|
||||
};
|
||||
|
||||
const getDeleteCommentUserAction = ({
|
||||
userAction,
|
||||
userProfiles,
|
||||
caseData,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
handleOutlineComment,
|
||||
}: {
|
||||
userAction: UserActionResponse<CommentUserAction>;
|
||||
} & Pick<UserActionBuilderArgs, 'handleOutlineComment' | 'userProfiles'>): EuiCommentProps[] => {
|
||||
const label = getDeleteLabelTitle(userAction);
|
||||
} & Pick<
|
||||
UserActionBuilderArgs,
|
||||
| 'handleOutlineComment'
|
||||
| 'userProfiles'
|
||||
| 'externalReferenceAttachmentTypeRegistry'
|
||||
| 'persistableStateAttachmentTypeRegistry'
|
||||
| 'caseData'
|
||||
>): EuiCommentProps[] => {
|
||||
const label = getDeleteLabelTitle({
|
||||
userAction,
|
||||
caseData,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
});
|
||||
|
||||
const commonBuilder = createCommonUpdateUserActionBuilder({
|
||||
userAction,
|
||||
userProfiles,
|
||||
|
@ -193,8 +274,11 @@ export const createCommentUserActionBuilder: UserActionBuilder = ({
|
|||
if (commentUserAction.action === Actions.delete) {
|
||||
return getDeleteCommentUserAction({
|
||||
userAction: commentUserAction,
|
||||
caseData,
|
||||
handleOutlineComment,
|
||||
userProfiles,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
*/
|
||||
|
||||
import React, { Suspense } from 'react';
|
||||
import { memoize } from 'lodash';
|
||||
import { memoize, partition } from 'lodash';
|
||||
|
||||
import { EuiCallOut, EuiCode, EuiLoadingSpinner } from '@elastic/eui';
|
||||
import { EuiCallOut, EuiCode, EuiLoadingSpinner, EuiButtonIcon, EuiFlexItem } from '@elastic/eui';
|
||||
import type { AttachmentType } from '../../../client/attachment_framework/types';
|
||||
import type { AttachmentTypeRegistry } from '../../../../common/registry';
|
||||
import type { CommentResponse } from '../../../../common/api';
|
||||
|
@ -116,6 +116,11 @@ export const createRegisteredAttachmentUserActionBuilder = <
|
|||
caseData: { id: caseData.id, title: caseData.title },
|
||||
};
|
||||
|
||||
const actions = attachmentViewObject.getActions?.(props) ?? [];
|
||||
const [primaryActions, nonPrimaryActions] = partition(actions, 'isPrimary');
|
||||
const visiblePrimaryActions = primaryActions.slice(0, 2);
|
||||
const nonVisiblePrimaryActions = primaryActions.slice(2, primaryActions.length);
|
||||
|
||||
return [
|
||||
{
|
||||
username: (
|
||||
|
@ -128,10 +133,24 @@ export const createRegisteredAttachmentUserActionBuilder = <
|
|||
timelineAvatar: attachmentViewObject.timelineAvatar,
|
||||
actions: (
|
||||
<UserActionContentToolbar id={comment.id}>
|
||||
{attachmentViewObject.actions}
|
||||
{visiblePrimaryActions.map((action) => (
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
data-test-subj={`attachment-${attachmentTypeId}-${comment.id}`}
|
||||
>
|
||||
<EuiButtonIcon
|
||||
aria-label={action.label}
|
||||
iconType={action.iconType}
|
||||
color={action.color ?? 'text'}
|
||||
onClick={action.onClick}
|
||||
data-test-subj={`attachment-${attachmentTypeId}-${comment.id}-${action.iconType}`}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
<RegisteredAttachmentsPropertyActions
|
||||
isLoading={isLoading}
|
||||
onDelete={() => handleDeleteComment(comment.id, DELETE_REGISTERED_ATTACHMENT)}
|
||||
registeredAttachmentActions={[...nonVisiblePrimaryActions, ...nonPrimaryActions]}
|
||||
/>
|
||||
</UserActionContentToolbar>
|
||||
),
|
||||
|
|
|
@ -21,7 +21,7 @@ const UserActionContentToolbarComponent: React.FC<UserActionContentToolbarProps>
|
|||
withCopyLinkAction = true,
|
||||
children,
|
||||
}) => (
|
||||
<EuiFlexGroup responsive={false} alignItems="center">
|
||||
<EuiFlexGroup responsive={false} alignItems="center" gutterSize="m">
|
||||
{withCopyLinkAction ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<UserActionCopyLink id={id} />
|
||||
|
|
|
@ -22,6 +22,7 @@ describe('RegisteredAttachmentsPropertyActions', () => {
|
|||
|
||||
const props = {
|
||||
isLoading: false,
|
||||
registeredAttachmentActions: [],
|
||||
onDelete: jest.fn(),
|
||||
};
|
||||
|
||||
|
@ -95,4 +96,21 @@ describe('RegisteredAttachmentsPropertyActions', () => {
|
|||
|
||||
expect(result.getByTestId('property-actions-user-action')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders correctly registered attachments', async () => {
|
||||
const onClick = jest.fn();
|
||||
const action = [{ label: 'My button', iconType: 'download', onClick }];
|
||||
|
||||
const result = appMock.render(
|
||||
<RegisteredAttachmentsPropertyActions {...props} registeredAttachmentActions={action} />
|
||||
);
|
||||
|
||||
expect(result.getByTestId('property-actions-user-action')).toBeInTheDocument();
|
||||
|
||||
userEvent.click(result.getByTestId('property-actions-user-action-ellipses'));
|
||||
await waitForEuiPopoverOpen();
|
||||
|
||||
expect(result.getByTestId('property-actions-user-action-group').children.length).toBe(2);
|
||||
expect(result.queryByTestId('property-actions-user-action-download')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import type { AttachmentAction } from '../../../client/attachment_framework/types';
|
||||
import { useCasesContext } from '../../cases_context/use_cases_context';
|
||||
import * as i18n from './translations';
|
||||
import { UserActionPropertyActions } from './property_actions';
|
||||
|
@ -14,11 +15,13 @@ import { useDeletePropertyAction } from './use_delete_property_action';
|
|||
|
||||
interface Props {
|
||||
isLoading: boolean;
|
||||
registeredAttachmentActions: AttachmentAction[];
|
||||
onDelete: () => void;
|
||||
}
|
||||
|
||||
const RegisteredAttachmentsPropertyActionsComponent: React.FC<Props> = ({
|
||||
isLoading,
|
||||
registeredAttachmentActions,
|
||||
onDelete,
|
||||
}) => {
|
||||
const { permissions } = useCasesContext();
|
||||
|
@ -40,8 +43,9 @@ const RegisteredAttachmentsPropertyActionsComponent: React.FC<Props> = ({
|
|||
},
|
||||
]
|
||||
: []),
|
||||
...registeredAttachmentActions,
|
||||
];
|
||||
}, [permissions.delete, onModalOpen]);
|
||||
}, [permissions.delete, onModalOpen, registeredAttachmentActions]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -75,7 +75,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
|
|||
const validateAttachment = async (type: string, attachmentId?: string) => {
|
||||
await testSubjects.existOrFail(`comment-${type}-.test`);
|
||||
await testSubjects.existOrFail(`copy-link-${attachmentId}`);
|
||||
await testSubjects.existOrFail('test-attachment-action');
|
||||
await testSubjects.existOrFail(`attachment-.test-${attachmentId}-arrowRight`);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,23 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { lazy } from 'react';
|
||||
import { EuiButtonIcon } from '@elastic/eui';
|
||||
import { lazy } from 'react';
|
||||
import { ExternalReferenceAttachmentType } from '@kbn/cases-plugin/public/client/attachment_framework/types';
|
||||
|
||||
const AttachmentContentLazy = lazy(() => import('./external_references_content'));
|
||||
|
||||
const AttachmentActions: React.FC = () => {
|
||||
return (
|
||||
<EuiButtonIcon
|
||||
data-test-subj="test-attachment-action"
|
||||
onClick={() => {}}
|
||||
iconType="arrowRight"
|
||||
aria-label="See attachment"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const getExternalReferenceAttachmentRegular = (): ExternalReferenceAttachmentType => ({
|
||||
id: '.test',
|
||||
icon: 'casesApp',
|
||||
|
@ -29,7 +17,9 @@ export const getExternalReferenceAttachmentRegular = (): ExternalReferenceAttach
|
|||
getAttachmentViewObject: () => ({
|
||||
event: 'added a chart',
|
||||
timelineAvatar: 'casesApp',
|
||||
actions: <AttachmentActions />,
|
||||
getActions: () => [
|
||||
{ label: 'See attachment', onClick: () => {}, isPrimary: true, iconType: 'arrowRight' },
|
||||
],
|
||||
children: AttachmentContentLazy,
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -11,20 +11,8 @@ import {
|
|||
PersistableStateAttachmentType,
|
||||
PersistableStateAttachmentViewProps,
|
||||
} from '@kbn/cases-plugin/public/client/attachment_framework/types';
|
||||
import { EuiButtonIcon } from '@elastic/eui';
|
||||
import { EmbeddableComponentProps, TypedLensByValueInput } from '@kbn/lens-plugin/public';
|
||||
|
||||
const AttachmentActions: React.FC = () => {
|
||||
return (
|
||||
<EuiButtonIcon
|
||||
data-test-subj="test-attachment-action"
|
||||
onClick={() => {}}
|
||||
iconType="arrowRight"
|
||||
aria-label="See attachment"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const getLazyComponent = (
|
||||
EmbeddableComponent: React.ComponentType<EmbeddableComponentProps>
|
||||
): React.LazyExoticComponent<React.FC<PersistableStateAttachmentViewProps>> =>
|
||||
|
@ -61,7 +49,9 @@ export const getPersistableStateAttachmentRegular = (
|
|||
getAttachmentViewObject: () => ({
|
||||
event: 'added an embeddable',
|
||||
timelineAvatar: 'casesApp',
|
||||
actions: <AttachmentActions />,
|
||||
getActions: () => [
|
||||
{ label: 'See attachment', onClick: () => {}, isPrimary: true, iconType: 'arrowRight' },
|
||||
],
|
||||
children: getLazyComponent(EmbeddableComponent),
|
||||
}),
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue