Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { createMemoryHistory, History as HistoryPackageHistoryInterface } from 'history';
import copy from 'copy-to-clipboard';
import { noAncestorsTwoChildrenWithRelatedEventsOnOrigin } from '../data_access_layer/mocks/no_ancestors_two_children_with_related_events_on_origin';
import { Simulator } from '../test_utilities/simulator';
// Extend jest with a custom matcher
Expand All @@ -14,10 +13,6 @@ import { urlSearch } from '../test_utilities/url_search';
// the resolver component instance ID, used by the react code to distinguish piece of global state from those used by other resolver instances
const resolverComponentInstanceID = 'resolverComponentInstanceID';

jest.mock('copy-to-clipboard', () => {
return jest.fn();
});

describe(`Resolver: when analyzing a tree with no ancestors and two children and two related registry event on the origin, and when the component instance ID is ${resolverComponentInstanceID}`, () => {
/**
* Get (or lazily create and get) the simulator.
Expand Down Expand Up @@ -121,8 +116,8 @@ describe(`Resolver: when analyzing a tree with no ancestors and two children and

copyableFields?.map((copyableField) => {
copyableField.simulate('mouseenter');
simulator().testSubject('clipboard').last().simulate('click');
expect(copy).toHaveBeenLastCalledWith(copyableField.text(), expect.any(Object));
simulator().testSubject('resolver:panel:clipboard').last().simulate('click');
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(copyableField.text());
copyableField.simulate('mouseleave');
});
});
Expand Down Expand Up @@ -179,8 +174,8 @@ describe(`Resolver: when analyzing a tree with no ancestors and two children and

copyableFields?.map((copyableField) => {
copyableField.simulate('mouseenter');
simulator().testSubject('clipboard').last().simulate('click');
expect(copy).toHaveBeenLastCalledWith(copyableField.text(), expect.any(Object));
simulator().testSubject('resolver:panel:clipboard').last().simulate('click');
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(copyableField.text());
copyableField.simulate('mouseleave');
});
});
Expand Down Expand Up @@ -288,8 +283,8 @@ describe(`Resolver: when analyzing a tree with no ancestors and two children and

copyableFields?.map((copyableField) => {
copyableField.simulate('mouseenter');
simulator().testSubject('clipboard').last().simulate('click');
expect(copy).toHaveBeenLastCalledWith(copyableField.text(), expect.any(Object));
simulator().testSubject('resolver:panel:clipboard').last().simulate('click');
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(copyableField.text());
copyableField.simulate('mouseleave');
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

/* eslint-disable react/display-name */

import { EuiToolTip, EuiPopover } from '@elastic/eui';
import { EuiToolTip, EuiButtonIcon, EuiPopover } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import styled from 'styled-components';
import React, { memo, useState } from 'react';
import { WithCopyToClipboard } from '../../../common/lib/clipboard/with_copy_to_clipboard';
import React, { memo, useState, useCallback } from 'react';
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
import { useColors } from '../use_colors';
import { StyledPanel } from '../styles';

Expand Down Expand Up @@ -43,8 +43,10 @@ export const CopyablePanelField = memo(
({ textToCopy, content }: { textToCopy: string; content: JSX.Element | string }) => {
const { linkColor, copyableFieldBackground } = useColors();
const [isOpen, setIsOpen] = useState(false);
const toasts = useKibana().services.notifications?.toasts;

const onMouseEnter = () => setIsOpen(true);
const onMouseLeave = () => setIsOpen(false);

const ButtonContent = memo(() => (
<StyledCopyableField
Expand All @@ -57,7 +59,22 @@ export const CopyablePanelField = memo(
</StyledCopyableField>
));

const onMouseLeave = () => setIsOpen(false);
const onClick = useCallback(
async (event: React.MouseEvent<HTMLButtonElement>) => {
try {
await navigator.clipboard.writeText(textToCopy);
} catch (error) {
if (toasts) {
toasts.addError(error, {
title: i18n.translate('xpack.securitySolution.resolver.panel.copyFailureTitle', {
defaultMessage: 'Copy Failure',
}),
});
}
}
},
[textToCopy, toasts]
);

return (
<div onMouseLeave={onMouseLeave}>
Expand All @@ -74,10 +91,14 @@ export const CopyablePanelField = memo(
defaultMessage: 'Copy to Clipboard',
})}
>
<WithCopyToClipboard
data-test-subj="resolver:panel:copy-to-clipboard"
text={textToCopy}
titleSummary={textToCopy}
<EuiButtonIcon
aria-label={i18n.translate('xpack.securitySolution.resolver.panel.copyToClipboard', {
defaultMessage: 'Copy to Clipboard',
})}
color="text"
data-test-subj="resolver:panel:clipboard"
iconType="copyClipboard"
onClick={onClick}
/>
</EuiToolTip>
</EuiPopover>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ export const sideEffectSimulatorFactory: () => SideEffectSimulator = () => {
return contentRectForElement(this);
});

/**
* Mock the global writeText method as it is not available in jsDOM and alows us to track what was copied
*/
const MockClipboard: Clipboard = {
writeText: jest.fn(),
readText: jest.fn(),
addEventListener: jest.fn(),
dispatchEvent: jest.fn(),
removeEventListener: jest.fn(),
};
// @ts-ignore navigator doesn't natively exist on global
global.navigator.clipboard = MockClipboard;
/**
* A mock implementation of `ResizeObserver` that works with our fake `getBoundingClientRect` and `simulateElementResize`
*/
Expand Down