Skip to content
3 changes: 3 additions & 0 deletions packages/eui/changelogs/upcoming/8758.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**Breaking changes**

- Added `tooltipProps` to `EuiCopy` which replaces spreading all props to `EuiToolTip`
99 changes: 86 additions & 13 deletions packages/eui/src/components/copy/copy.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,33 @@
*/

import React from 'react';
import { shallow } from 'enzyme';
import { render } from '../../test/rtl';
import { fireEvent } from '@testing-library/react';
import {
waitForEuiToolTipVisible,
waitForEuiToolTipHidden,
render,
} from '../../test/rtl';
import { requiredProps } from '../../test';

import { EuiCopy } from './copy';

describe('EuiCopy', () => {
const originalExecCommand = document.execCommand;

beforeAll(() => {
Object.defineProperty(document, 'execCommand', {
value: jest.fn(() => true),
writable: true,
});
});

afterAll(() => {
Object.defineProperty(document, 'execCommand', {
value: originalExecCommand,
writable: true,
});
});

it('renders', () => {
const { container } = render(
<EuiCopy textToCopy="some text" {...requiredProps}>
Expand All @@ -24,23 +44,76 @@ describe('EuiCopy', () => {
});

describe('props', () => {
test('beforeMessage', () => {
const component = shallow(
<EuiCopy textToCopy="some text" beforeMessage="copy this">
{(copy) => <button onClick={copy}>Click to copy input text</button>}
it('beforeMessage', async () => {
const beforeMessage = 'copy this';
const { getByRole, getByText } = render(
<EuiCopy textToCopy="some text" beforeMessage={beforeMessage}>
{() => (
<button onMouseOver={() => {}} onFocus={() => {}}>
Click to copy input text
</button>
)}
</EuiCopy>
);
// Simulate mouse over to show the tooltip
fireEvent.mouseOver(getByRole('button'));
await waitForEuiToolTipVisible();
// The beforeMessage should be shown in the tooltip
expect(getByText(beforeMessage)).toBeInTheDocument();
fireEvent.mouseOut(getByRole('button'));
await waitForEuiToolTipHidden();
});

it('afterMessage', async () => {
const afterMessage = 'successfully copied';
const { getByRole, getByText } = render(
<EuiCopy textToCopy="some text" afterMessage={afterMessage}>
{(copy) => (
<button onClick={copy} onMouseOver={() => {}} onFocus={() => {}}>
Click to copy input text
</button>
)}
</EuiCopy>
);
expect(component.state('tooltipText')).toBe('copy this');

// Simulate a click to copy the text
fireEvent.click(getByRole('button'));
fireEvent.mouseOver(getByRole('button'));
await waitForEuiToolTipVisible();
// The afterMessage should be shown after the copy action
expect(getByText(afterMessage)).toBeInTheDocument();
fireEvent.mouseOut(getByRole('button'));
await waitForEuiToolTipHidden();
});

test('afterMessage', () => {
const component = shallow<EuiCopy>(
<EuiCopy textToCopy="some text" afterMessage="successfuly copied">
{(copy) => <button onClick={copy}>Click to copy input text</button>}
it('tooltipProps', async () => {
const tooltipProps = {
'data-test-subj': 'customTooltip',
className: 'myTooltipClass',
};
const beforeMessage = 'copy this';
const { getByRole, getByTestSubject } = render(
<EuiCopy
textToCopy="some text"
beforeMessage={beforeMessage}
tooltipProps={tooltipProps}
>
{() => (
<button onMouseOver={() => {}} onFocus={() => {}}>
Click to copy input text
</button>
)}
</EuiCopy>
);
const instance = component.instance();
expect(instance.props.afterMessage).toBe('successfuly copied');
// Simulate mouse over to show the tooltip
fireEvent.mouseOver(getByRole('button'));
await waitForEuiToolTipVisible();
// The tooltip portalled, so search the global document
const tooltip = getByTestSubject('customTooltip');
expect(tooltip).toBeInTheDocument();
expect(tooltip?.className).toContain('myTooltipClass');
fireEvent.mouseOut(getByRole('button'));
await waitForEuiToolTipHidden();
});
});
});
15 changes: 9 additions & 6 deletions packages/eui/src/components/copy/copy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ import { CommonProps } from '../common';
import { copyToClipboard } from '../../services';
import { EuiToolTip, EuiToolTipProps } from '../tool_tip';

export interface EuiCopyProps
extends CommonProps,
Partial<Omit<EuiToolTipProps, 'children'>> {
export interface EuiCopyProps extends CommonProps {
/**
* Text that will be copied to clipboard when copy function is executed.
*/
Expand All @@ -32,6 +30,12 @@ export interface EuiCopyProps
* Use your own logic to create the component that users interact with when triggering copy.
*/
children(copy: () => void): ReactElement;
/**
* Optional props to pass to the EuiToolTip component.
*/
tooltipProps?: Partial<
Omit<EuiToolTipProps, 'children' | 'content' | 'onMouseOut'>
>;
}

interface EuiCopyState {
Expand Down Expand Up @@ -67,16 +71,15 @@ export class EuiCopy extends Component<EuiCopyProps, EuiCopyState> {
};

render() {
const { children, textToCopy, beforeMessage, afterMessage, ...rest } =
this.props;
const { children, tooltipProps } = this.props;

return (
// See `src/components/tool_tip/tool_tip.js` for explanation of below eslint-disable
// eslint-disable-next-line jsx-a11y/mouse-events-have-key-events
<EuiToolTip
content={this.state.tooltipText}
onMouseOut={this.resetTooltipText}
{...rest}
{...tooltipProps}
>
{children(this.copy)}
</EuiToolTip>
Expand Down
16 changes: 8 additions & 8 deletions packages/website/docs/components/display/icons/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { iconSizes, iconSizesText } from './icon_sizes';
<EuiFlexGrid direction="row" columns={3}>
{iconSizes.map((size, index) => (
<EuiFlexItem key={size}>
<EuiCopy display="block" textToCopy={size} afterMessage={`${size} copied`}>
<EuiCopy textToCopy={size} afterMessage={`${size} copied`} tooltipProps={{ display: 'block' }}>
{(copy) => (
<EuiPanel hasShadow={false} hasBorder={false} onClick={copy} paddingSize="s">
<EuiIcon className="eui-alignMiddle" type="logoElasticsearch" size={size} /> &emsp;{' '}
Expand Down Expand Up @@ -69,7 +69,7 @@ import { iconColors } from './icon_colors.ts'
<EuiFlexGrid direction="row" columns={3}>
{iconColors.map((color) => (
<EuiFlexItem key={color}>
<EuiCopy display="block" textToCopy={color} afterMessage={`${color} copied`}>
<EuiCopy textToCopy={color} afterMessage={`${color} copied`} tooltipProps={{ display: 'block' }}>
{(copy) => {
const panel = (
<EuiPanel hasShadow={false} hasBorder={false} onClick={copy} paddingSize="s">
Expand Down Expand Up @@ -107,7 +107,7 @@ import { iconTypes } from './icon_types';
<EuiFlexGrid direction="row" columns={3}>
{iconTypes.map((iconType) => (
<EuiFlexItem key={iconType}>
<EuiCopy display="block" textToCopy={iconType} afterMessage={`${iconType} copied`}>
<EuiCopy textToCopy={iconType} afterMessage={`${iconType} copied`} tooltipProps={{ display: 'block' }}>
{(copy) => (
<EuiPanel hasShadow={false} hasBorder={false} onClick={copy} paddingSize="s">
<EuiIcon className="eui-alignMiddle" type={iconType} /> &emsp;{' '}
Expand All @@ -134,7 +134,7 @@ import { iconTypesEditor } from './icon_types_editor';
<EuiFlexGrid direction="row" columns={3}>
{iconTypes.map((iconType) => (
<EuiFlexItem key={iconType}>
<EuiCopy display="block" textToCopy={iconType} afterMessage={`${iconType} copied`}>
<EuiCopy textToCopy={iconType} afterMessage={`${iconType} copied`} tooltipProps={{ display: 'block' }}>
{(copy) => (
<EuiPanel hasShadow={false} hasBorder={false} onClick={copy} paddingSize="s">
<EuiIcon className="eui-alignMiddle" type={iconType} /> &emsp;{' '}
Expand Down Expand Up @@ -196,7 +196,7 @@ import { iconTypesLogos } from './icon_types_logos';
<EuiFlexGrid direction="row" columns={3}>
{iconTypes.map((iconType) => (
<EuiFlexItem key={iconType}>
<EuiCopy display="block" textToCopy={iconType} afterMessage={`${iconType} copied`}>
<EuiCopy textToCopy={iconType} afterMessage={`${iconType} copied`} tooltipProps={{ display: 'block' }}>
{(copy) => (
<EuiPanel hasShadow={false} hasBorder={false} onClick={copy} paddingSize="s" color="transparent">
<EuiIcon
Expand Down Expand Up @@ -237,7 +237,7 @@ import { iconTypesApps } from './icon_types_apps';
<EuiFlexGrid direction="row" columns={3}>
{iconTypes.map((iconType) => (
<EuiFlexItem key={iconType}>
<EuiCopy display="block" textToCopy={iconType} afterMessage={`${iconType} copied`}>
<EuiCopy textToCopy={iconType} afterMessage={`${iconType} copied`} tooltipProps={{ display: 'block' }}>
{(copy) => (
<EuiPanel hasShadow={false} hasBorder={false} onClick={copy} paddingSize="s">
<EuiIcon className="eui-alignMiddle" type={iconType} size="xl" /> &emsp;{' '}
Expand Down Expand Up @@ -268,7 +268,7 @@ import { iconTypesML } from './icon_types_ml';
<EuiFlexGrid direction="row" columns={3}>
{iconTypes.map((iconType) => (
<EuiFlexItem key={iconType}>
<EuiCopy display="block" textToCopy={iconType} afterMessage={`${iconType} copied`}>
<EuiCopy textToCopy={iconType} afterMessage={`${iconType} copied`} tooltipProps={{ display: 'block' }}>
{(copy) => (
<EuiPanel hasShadow={false} hasBorder={false} onClick={copy} paddingSize="s">
<EuiIcon className="eui-alignMiddle" type={iconType} size="xl" /> &emsp;{' '}
Expand Down Expand Up @@ -299,7 +299,7 @@ import { iconTypesTokens } from './icon_types_tokens';
<EuiFlexGrid direction="row" columns={3}>
{iconTypes.map((iconType) => (
<EuiFlexItem key={iconType}>
<EuiCopy display="block" textToCopy={iconType} afterMessage={`${iconType} copied`}>
<EuiCopy textToCopy={iconType} afterMessage={`${iconType} copied`} tooltipProps={{ display: 'block' }}>
{(copy) => (
<EuiPanel hasShadow={false} hasBorder={false} onClick={copy} paddingSize="s">
<EuiToken className="eui-alignMiddle" iconType={iconType} /> &emsp;{' '}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ export default () => {
};

const items = [
<EuiCopy textToCopy="Copied some text!" anchorClassName="eui-fullWidth">
<EuiCopy
textToCopy="Copied some text!"
tooltipProps={{ anchorClassName: 'eui-fullWidth' }}
>
{(copy) => (
<EuiContextMenuItem key="copy" icon="copy" onClick={copy} size="s">
Copy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ export default () => {
};

const items = [
<EuiCopy textToCopy="Copied some text!" anchorClassName="eui-fullWidth">
<EuiCopy textToCopy="Copied some text!" tooltipProps={{ anchorClassName: "eui-fullWidth" }}>
{(copy) => (
<EuiContextMenuItem key="copy" icon="copy" onClick={copy} size="s">
Copy
Expand Down