Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(designer): Render copied tooltip properly #6463

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions e2e/designer/mock-copypastescope.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ test(
});
await page.getByTestId('msla-copy-menu-option').click();

await expect(page.getByRole('tooltip', { name: 'Copied!' })).toBeVisible();
await expect(page.getByTestId('msla-tooltip-location-condition')).toBeVisible();
await page.getByTestId('msla-plus-button-initialize_variable-condition').click();
await page.getByTestId('msla-paste-button-initialize_variable-condition').click();
await page.waitForTimeout(1000);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ const DefaultNode = ({ targetPosition = Position.Top, sourcePosition = Position.
isLoadingDynamicData={isLoadingDynamicData}
nodeIndex={nodeIndex}
/>
{showCopyCallout ? <CopyTooltip targetRef={ref} hideTooltip={clearCopyTooltip} /> : null}
{showCopyCallout ? <CopyTooltip id={id} targetRef={ref} hideTooltip={clearCopyTooltip} /> : null}
<Handle className="node-handle bottom" type="source" position={sourcePosition} isConnectable={false} />
</div>
{showLeafComponents ? (
Expand Down
2 changes: 1 addition & 1 deletion libs/designer/src/lib/ui/CustomNodes/ScopeCardNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ const ScopeCardNode = ({ data, targetPosition = Position.Top, sourcePosition = P
setFocus={shouldFocus}
nodeIndex={nodeIndex}
/>
{showCopyCallout ? <CopyTooltip targetRef={rootRef} hideTooltip={clearCopyCallout} /> : null}
{showCopyCallout ? <CopyTooltip id={scopeId} targetRef={rootRef} hideTooltip={clearCopyCallout} /> : null}
{normalizedType === constants.NODE.TYPE.FOREACH && isMonitoringView ? renderLoopsPager : null}
<Handle className="node-handle bottom" type="source" position={sourcePosition} isConnectable={false} />
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
/* eslint-disable react/display-name */
import { Tooltip } from '@fluentui/react-components';
import { replaceWhiteSpaceWithUnderscore } from '@microsoft/logic-apps-shared';
import { useOnViewportChange } from '@xyflow/react';
import { useMemo, useRef } from 'react';
import { useRef } from 'react';
import { useIntl } from 'react-intl';

export interface CopyTooltipProps {
targetRef?: React.RefObject<HTMLElement>;
location?: { x: number; y: number };
hideTooltip: () => void;
id: string;
}

export const CopyTooltip = ({ targetRef: ref, location, hideTooltip }: CopyTooltipProps) => {
export const CopyTooltip = ({ targetRef: ref, location, hideTooltip, id }: CopyTooltipProps) => {
useOnViewportChange({ onStart: () => hideTooltip() });

const intl = useIntl();
Expand All @@ -22,21 +23,20 @@ export const CopyTooltip = ({ targetRef: ref, location, hideTooltip }: CopyToolt

const locationRef = useRef<HTMLDivElement>(null);

const TooltipComponent = useMemo(
() => () => (
<Tooltip
positioning={{ target: (ref ?? locationRef)?.current, position: 'below', align: 'start' }}
content={copiedText}
relationship="description"
visible={true}
/>
),
[copiedText, ref]
);

return (
<div ref={locationRef} style={{ position: 'absolute', top: location?.y ?? 0, left: location?.x ?? 0 }}>
<TooltipComponent />
</div>
<Tooltip
key={id}
positioning={{ target: (ref ?? locationRef)?.current ?? undefined, position: 'below', align: 'start' }}
content={copiedText}
relationship="description"
visible={true}
>
<div
data-testid={`msla-tooltip-location-${replaceWhiteSpaceWithUnderscore(id)}`}
data-automation-id={`msla-tooltip-location-${replaceWhiteSpaceWithUnderscore(id)}`}
ref={locationRef}
style={{ width: '1px', height: '1px', position: 'absolute', top: location?.y ?? 0, left: location?.x ?? 0 }}
/>
</Tooltip>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ export const DesignerContextualMenu = () => {
title={title}
setOpen={(o) => setOpen(o)}
/>
{showCopyCallout ? <CopyTooltip location={menuData?.location} hideTooltip={clearCopyCallout} /> : null}
{showCopyCallout ? <CopyTooltip id={nodeId} location={menuData?.location} hideTooltip={clearCopyCallout} /> : null}
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { render, screen } from '@testing-library/react';
import { CopyTooltip, CopyTooltipProps } from '../CopyTooltip';
import { describe, it, expect, vi, afterEach, beforeAll } from 'vitest';
import React from 'react';

const hideTooltipMock = vi.fn();

vi.mock('@xyflow/react', async () => {
const actualIntl = await vi.importActual('@xyflow/react');
return {
...actualIntl,
useOnViewportChange: vi.fn(() => ({ onStart: hideTooltipMock })),
};
});

vi.mock('react-intl', async () => {
const actualIntl = await vi.importActual('react-intl');
return {
...actualIntl,
useIntl: () => ({
formatMessage: vi.fn(() => 'Copied!'),
}),
};
});

describe('CopyTooltip', () => {
beforeAll(() => {
const portalRoot = document.createElement('div');
portalRoot.setAttribute('id', 'root');
document.body.appendChild(portalRoot);
});

afterEach(() => {
vi.clearAllMocks();
});

const renderComponent = (props: Partial<CopyTooltipProps> = {}) => {
const defaultProps: CopyTooltipProps = {
hideTooltip: hideTooltipMock,
id: 'test-id',
...props,
};

return render(<CopyTooltip {...defaultProps} />);
};

it('should render the tooltip with the correct content', () => {
const copiedText = 'Copied!';
const location = { x: 100, y: 100 };

// Render the Tooltip component
const { baseElement } = renderComponent({ location });

// Get the tooltip div and the location div
const tooltipDiv = screen.getByRole('tooltip');
const tooltipLocationDiv = screen.getByTestId('msla-tooltip-location-test_id');

// Assert that the tooltip and location div are rendered correctly
expect(tooltipDiv).toBeInTheDocument();
expect(screen.getByText(copiedText)).toBeVisible();
expect(tooltipLocationDiv).toBeInTheDocument();
expect(baseElement).toMatchSnapshot();
});

it('should position the tooltip based on the provided location', () => {
const location = { x: 100, y: 100 };

// Render the Tooltip component
const { baseElement } = renderComponent({ location });

// Get the tooltip location div
const tooltipLocationDiv = screen.getByTestId('msla-tooltip-location-test_id');

// Assert that the location div are rendered correctly
expect(tooltipLocationDiv).toBeInTheDocument();
expect(tooltipLocationDiv).toHaveStyle({ position: 'absolute', top: '100px', left: '100px' });
expect(baseElement).toMatchSnapshot();
});

it('should default to locationRef if targetRef is not provided', () => {
renderComponent();

const tooltipDiv = screen.getByRole('tooltip').parentElement;
expect(tooltipDiv).toHaveStyle({ position: 'absolute', top: '0px', left: '0px' });
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`CopyTooltip > should position the tooltip based on the provided location 1`] = `
<body>
<div
id="root"
/>
<div>
<div
aria-describedby="tooltip-r1"
data-automation-id="msla-tooltip-location-test_id"
data-testid="msla-tooltip-location-test_id"
style="width: 1px; height: 1px; position: absolute; top: 100px; left: 100px;"
/>
<span
hidden=""
/>
</div>
<div
class="___1uxlrft_djprso0 f1euv43f f15twtuk f1vgc2s3 f1e31b4d f494woh"
data-portal-node="true"
dir="ltr"
>
<div
class="fui-Tooltip__content ___1iilip3_omhya70 ftgm304 f1ewtqcl f132xexn f158kwzp fk6fouc fy9rknc fwrc4pm fokg9q4 ft85np5 f9ggezi f1bzqsji fxugw4r f19n0e5 fxeb0a7"
id="tooltip-r1"
role="tooltip"
style="position: fixed; left: 0px; top: 0px; margin: 0px;"
>
Copied!
</div>
</div>
</body>
`;

exports[`CopyTooltip > should render the tooltip with the correct content 1`] = `
<body>
<div
id="root"
/>
<div>
<div
aria-describedby="tooltip-r0"
data-automation-id="msla-tooltip-location-test_id"
data-testid="msla-tooltip-location-test_id"
style="width: 1px; height: 1px; position: absolute; top: 100px; left: 100px;"
/>
<span
hidden=""
/>
</div>
<div
class="___1uxlrft_djprso0 f1euv43f f15twtuk f1vgc2s3 f1e31b4d f494woh"
data-portal-node="true"
dir="ltr"
>
<div
class="fui-Tooltip__content ___1iilip3_omhya70 ftgm304 f1ewtqcl f132xexn f158kwzp fk6fouc fy9rknc fwrc4pm fokg9q4 ft85np5 f9ggezi f1bzqsji fxugw4r f19n0e5 fxeb0a7"
id="tooltip-r0"
role="tooltip"
style="position: fixed; left: 0px; top: 0px; margin: 0px;"
>
Copied!
</div>
</div>
</body>
`;
Loading