mirror of
https://github.com/darkwire/darkwire.io.git
synced 2025-07-26 13:34:40 +00:00
Remove tooltip
This commit is contained in:
parent
218fbfbfd1
commit
8fe8abdcfc
@ -15,7 +15,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bootstrap": "^4.6.2",
|
"bootstrap": "^4.6.2",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
"clipboard": "^2.0.11",
|
|
||||||
"jquery": "3",
|
"jquery": "3",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"nanoid": "^4.0.0",
|
"nanoid": "^4.0.0",
|
||||||
@ -29,6 +28,7 @@
|
|||||||
"react-router": "^6.4.4",
|
"react-router": "^6.4.4",
|
||||||
"react-router-dom": "^6.4.4",
|
"react-router-dom": "^6.4.4",
|
||||||
"react-simple-dropdown": "^3.2.3",
|
"react-simple-dropdown": "^3.2.3",
|
||||||
|
"react-tooltip": "^5.2.0",
|
||||||
"redux": "^4.2.0",
|
"redux": "^4.2.0",
|
||||||
"redux-thunk": "^2.4.2",
|
"redux-thunk": "^2.4.2",
|
||||||
"sanitize-html": "^2.7.3",
|
"sanitize-html": "^2.7.3",
|
||||||
|
@ -62,10 +62,7 @@ exports[`Connected Home component > should display 1`] = `
|
|||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
class="btn btn-plain btn-link clipboard-trigger room-id ellipsis"
|
class="btn btn-plain btn-link clipboard-trigger room-id ellipsis"
|
||||||
data-clipboard-text="http://localhost:3000/"
|
id="copy-room-url-button"
|
||||||
data-placement="bottom"
|
|
||||||
data-toggle="tooltip"
|
|
||||||
title="Copied"
|
|
||||||
>
|
>
|
||||||
/
|
/
|
||||||
</button>
|
</button>
|
||||||
@ -74,9 +71,8 @@ exports[`Connected Home component > should display 1`] = `
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="lock-room btn btn-link btn-plain"
|
class="lock-room btn btn-link btn-plain"
|
||||||
data-placement="bottom"
|
data-testid="lock-room-button"
|
||||||
data-toggle="tooltip"
|
id="lock-room-button"
|
||||||
title="You must be the owner to lock or unlock the room"
|
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class="muted"
|
class="muted"
|
||||||
@ -164,8 +160,6 @@ exports[`Connected Home component > should display 1`] = `
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="me-icon-wrap"
|
class="me-icon-wrap"
|
||||||
data-placement="bottom"
|
|
||||||
data-toggle="tooltip"
|
|
||||||
title="Me"
|
title="Me"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
|
||||||
import { render, fireEvent, waitFor } from '@testing-library/react';
|
import { render, fireEvent, waitFor } from '@testing-library/react';
|
||||||
import mock$ from 'jquery';
|
import mock$ from 'jquery';
|
||||||
import { test, expect, vi } from 'vitest';
|
import { test, expect, vi } from 'vitest';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
|
|
||||||
import Nav from '.';
|
import Nav from '.';
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ vi.mock('nanoid', () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
vi.useFakeTimers();
|
const mockClipboardWriteTest = vi.fn();
|
||||||
|
|
||||||
const mockTranslations = {
|
const mockTranslations = {
|
||||||
newRoomButton: 'new room',
|
newRoomButton: 'new room',
|
||||||
@ -43,6 +43,8 @@ const mockTranslations = {
|
|||||||
aboutButton: 'about',
|
aboutButton: 'about',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
vi.useFakeTimers();
|
||||||
|
|
||||||
test('Nav component is displaying', async () => {
|
test('Nav component is displaying', async () => {
|
||||||
const { asFragment } = render(
|
const { asFragment } = render(
|
||||||
<Nav
|
<Nav
|
||||||
@ -58,10 +60,6 @@ test('Nav component is displaying', async () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(asFragment()).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
|
|
||||||
expect(mock$).toHaveBeenCalledWith('.room-id');
|
|
||||||
expect(mock$).toHaveBeenLastCalledWith('.lock-room');
|
|
||||||
expect(mockTooltip).toHaveBeenLastCalledWith({ trigger: 'manual' });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Nav component is displaying with another configuration and can rerender', async () => {
|
test('Nav component is displaying with another configuration and can rerender', async () => {
|
||||||
@ -103,11 +101,11 @@ test('Nav component is displaying with another configuration and can rerender',
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Can copy room url', async () => {
|
test('Can copy room url', async () => {
|
||||||
document.execCommand = vi.fn(() => true);
|
navigator.clipboard = { writeText: mockClipboardWriteTest };
|
||||||
|
|
||||||
const toggleLockRoom = vi.fn();
|
const toggleLockRoom = vi.fn();
|
||||||
|
|
||||||
const { getByText } = render(
|
const { getByText, queryByText } = render(
|
||||||
<Nav
|
<Nav
|
||||||
members={[
|
members={[
|
||||||
{ id: 'id1', username: 'alan', isOwner: true },
|
{ id: 'id1', username: 'alan', isOwner: true },
|
||||||
@ -119,27 +117,28 @@ test('Can copy room url', async () => {
|
|||||||
toggleLockRoom={toggleLockRoom}
|
toggleLockRoom={toggleLockRoom}
|
||||||
openModal={() => {}}
|
openModal={() => {}}
|
||||||
iAmOwner={false}
|
iAmOwner={false}
|
||||||
translations={{}}
|
translations={{ copyButtonTooltip: 'Copied' }}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fireEvent.click(getByText(`/testRoom`));
|
await act(async () => {
|
||||||
|
await fireEvent.click(getByText('/testRoom'));
|
||||||
|
});
|
||||||
|
|
||||||
expect(document.execCommand).toHaveBeenLastCalledWith('copy');
|
expect(mockClipboardWriteTest).toHaveBeenLastCalledWith('http://localhost:3000/testRoom');
|
||||||
expect(mock$).toHaveBeenCalledTimes(12);
|
|
||||||
expect(mockTooltip).toHaveBeenLastCalledWith('show');
|
await getByText('Copied');
|
||||||
|
|
||||||
// Wait tooltip closing
|
// Wait tooltip closing
|
||||||
vi.runAllTimers();
|
await act(() => vi.runAllTimers());
|
||||||
|
|
||||||
expect(mock$).toHaveBeenCalledTimes(15);
|
expect(queryByText('Copied')).not.toBeInTheDocument();
|
||||||
expect(mockTooltip).toHaveBeenLastCalledWith('hide');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can lock/unlock room is room owner only', async () => {
|
test('Can lock/unlock room is room owner only', async () => {
|
||||||
const toggleLockRoom = vi.fn();
|
const toggleLockRoom = vi.fn();
|
||||||
|
|
||||||
const { rerender, getByTitle } = render(
|
const { rerender, getByTestId, getByText, queryByText } = render(
|
||||||
<Nav
|
<Nav
|
||||||
members={[
|
members={[
|
||||||
{ id: 'id1', username: 'alan', isOwner: true },
|
{ id: 'id1', username: 'alan', isOwner: true },
|
||||||
@ -155,13 +154,13 @@ test('Can lock/unlock room is room owner only', async () => {
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
const toggleLockRoomButton = getByTitle('You must be the owner to lock or unlock the room');
|
const toggleLockRoomButton = getByTestId('lock-room-button');
|
||||||
|
|
||||||
fireEvent.click(toggleLockRoomButton);
|
await fireEvent.click(toggleLockRoomButton);
|
||||||
|
|
||||||
expect(toggleLockRoom).toHaveBeenCalledWith();
|
expect(toggleLockRoom).toHaveBeenCalledWith();
|
||||||
|
|
||||||
fireEvent.click(toggleLockRoomButton);
|
await fireEvent.click(toggleLockRoomButton);
|
||||||
|
|
||||||
expect(toggleLockRoom).toHaveBeenCalledTimes(2);
|
expect(toggleLockRoom).toHaveBeenCalledTimes(2);
|
||||||
|
|
||||||
@ -182,11 +181,16 @@ test('Can lock/unlock room is room owner only', async () => {
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fireEvent.click(toggleLockRoomButton);
|
await fireEvent.click(toggleLockRoomButton);
|
||||||
|
|
||||||
expect(toggleLockRoom).toHaveBeenCalledTimes(2);
|
expect(toggleLockRoom).toHaveBeenCalledTimes(2);
|
||||||
expect(mock$).toHaveBeenLastCalledWith('.lock-room');
|
|
||||||
expect(mockTooltip).toHaveBeenLastCalledWith('show');
|
await getByText('You must be the owner to lock or unlock the room');
|
||||||
|
|
||||||
|
// Wait tooltip closing
|
||||||
|
await act(() => vi.runAllTimers());
|
||||||
|
|
||||||
|
expect(queryByText('You must be the owner to lock or unlock the room')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can show user list', async () => {
|
test('Can show user list', async () => {
|
||||||
@ -226,8 +230,10 @@ test('Can show user list', async () => {
|
|||||||
translations={{}}
|
translations={{}}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
await waitFor(() => expect(getByText('alan')).toBeInTheDocument());
|
await waitFor(() => expect(getByText('alan')).toBeInTheDocument());
|
||||||
await waitFor(() => expect(getByText('dan')).toBeInTheDocument());
|
await waitFor(() => expect(getByText('dan')).toBeInTheDocument());
|
||||||
|
|
||||||
expect(queryByTitle('Owner')).not.toBeInTheDocument();
|
expect(queryByTitle('Owner')).not.toBeInTheDocument();
|
||||||
expect(queryByTitle('Me')).not.toBeInTheDocument();
|
expect(queryByTitle('Me')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
@ -15,9 +15,7 @@ exports[`Nav component is displaying 1`] = `
|
|||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
class="btn btn-plain btn-link clipboard-trigger room-id ellipsis"
|
class="btn btn-plain btn-link clipboard-trigger room-id ellipsis"
|
||||||
data-clipboard-text="http://localhost:3000/testRoom"
|
id="copy-room-url-button"
|
||||||
data-placement="bottom"
|
|
||||||
data-toggle="tooltip"
|
|
||||||
>
|
>
|
||||||
/testRoom
|
/testRoom
|
||||||
</button>
|
</button>
|
||||||
@ -26,9 +24,8 @@ exports[`Nav component is displaying 1`] = `
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="lock-room btn btn-link btn-plain"
|
class="lock-room btn btn-link btn-plain"
|
||||||
data-placement="bottom"
|
data-testid="lock-room-button"
|
||||||
data-toggle="tooltip"
|
id="lock-room-button"
|
||||||
title="You must be the owner to lock or unlock the room"
|
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class="muted"
|
class="muted"
|
||||||
@ -257,9 +254,7 @@ exports[`Nav component is displaying with another configuration and can rerender
|
|||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
class="btn btn-plain btn-link clipboard-trigger room-id ellipsis"
|
class="btn btn-plain btn-link clipboard-trigger room-id ellipsis"
|
||||||
data-clipboard-text="http://localhost:3000/testRoom_2"
|
id="copy-room-url-button"
|
||||||
data-placement="bottom"
|
|
||||||
data-toggle="tooltip"
|
|
||||||
>
|
>
|
||||||
/testRoom_2
|
/testRoom_2
|
||||||
</button>
|
</button>
|
||||||
@ -268,9 +263,8 @@ exports[`Nav component is displaying with another configuration and can rerender
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="lock-room btn btn-link btn-plain"
|
class="lock-room btn btn-link btn-plain"
|
||||||
data-placement="bottom"
|
data-testid="lock-room-button"
|
||||||
data-toggle="tooltip"
|
id="lock-room-button"
|
||||||
title="You must be the owner to lock or unlock the room"
|
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
fill="none"
|
fill="none"
|
||||||
@ -357,9 +351,6 @@ exports[`Nav component is displaying with another configuration and can rerender
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="owner-icon-wrap"
|
class="owner-icon-wrap"
|
||||||
data-placement="bottom"
|
|
||||||
data-toggle="tooltip"
|
|
||||||
title="Owner"
|
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class="owner-icon"
|
class="owner-icon"
|
||||||
@ -369,6 +360,7 @@ exports[`Nav component is displaying with another configuration and can rerender
|
|||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
|
title="Owner"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
width="24"
|
width="24"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -545,9 +537,7 @@ exports[`Nav component is displaying with another configuration and can rerender
|
|||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
class="btn btn-plain btn-link clipboard-trigger room-id ellipsis"
|
class="btn btn-plain btn-link clipboard-trigger room-id ellipsis"
|
||||||
data-clipboard-text="http://localhost:3000/testRoom_3"
|
id="copy-room-url-button"
|
||||||
data-placement="bottom"
|
|
||||||
data-toggle="tooltip"
|
|
||||||
>
|
>
|
||||||
/testRoom_3
|
/testRoom_3
|
||||||
</button>
|
</button>
|
||||||
@ -556,9 +546,8 @@ exports[`Nav component is displaying with another configuration and can rerender
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="lock-room btn btn-link btn-plain"
|
class="lock-room btn btn-link btn-plain"
|
||||||
data-placement="bottom"
|
data-testid="lock-room-button"
|
||||||
data-toggle="tooltip"
|
id="lock-room-button"
|
||||||
title="You must be the owner to lock or unlock the room"
|
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
fill="none"
|
fill="none"
|
||||||
@ -645,9 +634,6 @@ exports[`Nav component is displaying with another configuration and can rerender
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="owner-icon-wrap"
|
class="owner-icon-wrap"
|
||||||
data-placement="bottom"
|
|
||||||
data-toggle="tooltip"
|
|
||||||
title="Owner"
|
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class="owner-icon"
|
class="owner-icon"
|
||||||
@ -657,6 +643,7 @@ exports[`Nav component is displaying with another configuration and can rerender
|
|||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
|
title="Owner"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
width="24"
|
width="24"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
@ -3,31 +3,23 @@ import PropTypes from 'prop-types';
|
|||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { Info, Settings, PlusCircle, User, Users, Lock, Unlock, Star } from 'react-feather';
|
import { Info, Settings, PlusCircle, User, Users, Lock, Unlock, Star } from 'react-feather';
|
||||||
import Dropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown';
|
import Dropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown';
|
||||||
import Clipboard from 'clipboard';
|
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
|
import { Tooltip } from 'react-tooltip';
|
||||||
|
|
||||||
import logoImg from '@/img/logo.png';
|
import logoImg from '@/img/logo.png';
|
||||||
import Username from '@/components/Username';
|
import Username from '@/components/Username';
|
||||||
|
|
||||||
const Nav = ({ members, roomId, userId, roomLocked, toggleLockRoom, openModal, iAmOwner, translations }) => {
|
const Nav = ({ members, roomId, userId, roomLocked, toggleLockRoom, openModal, iAmOwner, translations }) => {
|
||||||
|
const [showCopyTooltip, setShowCopyTooltip] = React.useState(false);
|
||||||
|
const [showLockedTooltip, setShowLockedTooltip] = React.useState(false);
|
||||||
|
const mountedRef = React.useRef(true);
|
||||||
|
const roomUrl = `${window.location.origin}/${roomId}`;
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const clip = new Clipboard('.clipboard-trigger');
|
mountedRef.current = true;
|
||||||
|
return () => {
|
||||||
clip.on('success', () => {
|
mountedRef.current = false;
|
||||||
$('.room-id').tooltip('show');
|
};
|
||||||
setTimeout(() => {
|
|
||||||
$('.room-id').tooltip('hide');
|
|
||||||
}, 3000);
|
|
||||||
});
|
|
||||||
|
|
||||||
$(() => {
|
|
||||||
$('.room-id').tooltip({
|
|
||||||
trigger: 'manual',
|
|
||||||
});
|
|
||||||
$('.lock-room').tooltip({
|
|
||||||
trigger: 'manual',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const newRoom = () => {
|
const newRoom = () => {
|
||||||
@ -47,39 +39,67 @@ const Nav = ({ members, roomId, userId, roomLocked, toggleLockRoom, openModal, i
|
|||||||
|
|
||||||
const handleToggleLock = () => {
|
const handleToggleLock = () => {
|
||||||
if (!iAmOwner) {
|
if (!iAmOwner) {
|
||||||
$('.lock-room').tooltip('show');
|
setShowLockedTooltip(true);
|
||||||
setTimeout(() => $('.lock-room').tooltip('hide'), 3000);
|
setTimeout(() => {
|
||||||
|
if (mountedRef.current) {
|
||||||
|
setShowLockedTooltip(false);
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
toggleLockRoom();
|
toggleLockRoom();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleCopy = async () => {
|
||||||
|
await navigator.clipboard.writeText(roomUrl);
|
||||||
|
setShowCopyTooltip(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
if (mountedRef.current) {
|
||||||
|
setShowCopyTooltip(false);
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className="navbar navbar-expand-md navbar-dark">
|
<nav className="navbar navbar-expand-md navbar-dark">
|
||||||
<div className="meta">
|
<div className="meta">
|
||||||
<img src={logoImg} alt="Darkwire" className="logo" />
|
<img src={logoImg} alt="Darkwire" className="logo" />
|
||||||
|
|
||||||
<button
|
<button
|
||||||
data-toggle="tooltip"
|
id="copy-room-url-button"
|
||||||
data-placement="bottom"
|
|
||||||
title={translations.copyButtonTooltip}
|
|
||||||
data-clipboard-text={`${window.location.origin}/${roomId}`}
|
|
||||||
className="btn btn-plain btn-link clipboard-trigger room-id ellipsis"
|
className="btn btn-plain btn-link clipboard-trigger room-id ellipsis"
|
||||||
|
onClick={handleCopy}
|
||||||
>
|
>
|
||||||
{`/${roomId}`}
|
{`/${roomId}`}
|
||||||
</button>
|
</button>
|
||||||
|
{showCopyTooltip && (
|
||||||
|
<Tooltip
|
||||||
|
anchorId="copy-room-url-button"
|
||||||
|
content={translations.copyButtonTooltip}
|
||||||
|
place="bottom"
|
||||||
|
events={[]}
|
||||||
|
isOpen={true}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<span className="lock-room-container">
|
<span className="lock-room-container">
|
||||||
<button
|
<button
|
||||||
|
id="lock-room-button"
|
||||||
|
data-testid="lock-room-button"
|
||||||
onClick={handleToggleLock}
|
onClick={handleToggleLock}
|
||||||
className="lock-room btn btn-link btn-plain"
|
className="lock-room btn btn-link btn-plain"
|
||||||
data-toggle="tooltip"
|
|
||||||
data-placement="bottom"
|
|
||||||
title="You must be the owner to lock or unlock the room"
|
|
||||||
>
|
>
|
||||||
{roomLocked && <Lock />}
|
{roomLocked && <Lock />}
|
||||||
{!roomLocked && <Unlock className="muted" />}
|
{!roomLocked && <Unlock className="muted" />}
|
||||||
</button>
|
</button>
|
||||||
|
{showLockedTooltip && (
|
||||||
|
<Tooltip
|
||||||
|
anchorId="lock-room-button"
|
||||||
|
content="You must be the owner to lock or unlock the room"
|
||||||
|
place="bottom"
|
||||||
|
events={[]}
|
||||||
|
isOpen={true}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<Dropdown className="members-dropdown">
|
<Dropdown className="members-dropdown">
|
||||||
@ -96,13 +116,13 @@ const Nav = ({ members, roomId, userId, roomLocked, toggleLockRoom, openModal, i
|
|||||||
<Username username={member.username} />
|
<Username username={member.username} />
|
||||||
<span className="icon-container">
|
<span className="icon-container">
|
||||||
{member.id === userId && (
|
{member.id === userId && (
|
||||||
<span data-toggle="tooltip" data-placement="bottom" title="Me" className="me-icon-wrap">
|
<span className="me-icon-wrap" title="Me">
|
||||||
<User className="me-icon" />
|
<User className="me-icon" />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{member.isOwner && (
|
{member.isOwner && (
|
||||||
<span data-toggle="tooltip" data-placement="bottom" title="Owner" className="owner-icon-wrap">
|
<span className="owner-icon-wrap">
|
||||||
<Star className="owner-icon" />
|
<Star className="owner-icon" title="Owner" />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import React from 'react';
|
|
||||||
import { render, fireEvent } from '@testing-library/react';
|
import { render, fireEvent } from '@testing-library/react';
|
||||||
import RoomLink from '.';
|
import RoomLink from '.';
|
||||||
import mock$ from 'jquery';
|
import { describe, it, expect, vi } from 'vitest';
|
||||||
import { describe, it, expect, vi, afterEach } from 'vitest';
|
import { act } from 'react-dom/test-utils';
|
||||||
|
|
||||||
const mockTooltip = vi.fn().mockImplementation(param => {});
|
const mockTooltip = vi.fn().mockImplementation(param => {});
|
||||||
|
|
||||||
@ -26,43 +25,31 @@ const mockTranslations = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
describe('RoomLink', () => {
|
describe('RoomLink', () => {
|
||||||
afterEach(() => {
|
|
||||||
mock$.mockClear();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display', async () => {
|
it('should display', async () => {
|
||||||
const { asFragment, unmount } = render(<RoomLink roomId="roomId" translations={mockTranslations} />);
|
const { asFragment } = render(<RoomLink roomId="roomId" translations={mockTranslations} />);
|
||||||
|
|
||||||
expect(asFragment()).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
|
|
||||||
expect(mock$).toHaveBeenLastCalledWith('.copy-room');
|
|
||||||
expect(mockTooltip).toHaveBeenLastCalledWith({ trigger: 'manual' });
|
|
||||||
mock$.mockClear();
|
|
||||||
|
|
||||||
unmount();
|
|
||||||
|
|
||||||
expect(mock$).toHaveBeenLastCalledWith('.copy-room');
|
|
||||||
expect(mockTooltip).toHaveBeenLastCalledWith('hide');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should copy link', async () => {
|
it('should copy link', async () => {
|
||||||
// Mock execCommand for paste
|
const mockClipboardWriteTest = vi.fn();
|
||||||
document.execCommand = vi.fn(() => true);
|
navigator.clipboard = { writeText: mockClipboardWriteTest };
|
||||||
|
|
||||||
const { getByTitle } = render(<RoomLink roomId="roomId" translations={mockTranslations} />);
|
const { getByTestId, queryByText, getByText } = render(
|
||||||
|
<RoomLink roomId="roomId" translations={mockTranslations} />,
|
||||||
|
);
|
||||||
|
|
||||||
await fireEvent.click(getByTitle(mockTranslations.copyButtonTooltip));
|
await act(async () => {
|
||||||
|
await fireEvent.click(getByTestId('copy-room-button'));
|
||||||
|
});
|
||||||
|
|
||||||
expect(document.execCommand).toHaveBeenLastCalledWith('copy');
|
expect(mockClipboardWriteTest).toHaveBeenLastCalledWith('http://localhost:3000/roomId');
|
||||||
expect(mock$).toHaveBeenCalledTimes(4);
|
|
||||||
expect(mock$).toHaveBeenLastCalledWith('.copy-room');
|
|
||||||
expect(mockTooltip).toHaveBeenLastCalledWith('show');
|
|
||||||
|
|
||||||
// Wait for tooltip to close
|
await getByText(mockTranslations.copyButtonTooltip);
|
||||||
vi.runAllTimers();
|
|
||||||
|
|
||||||
expect(mock$).toHaveBeenCalledTimes(6);
|
// Wait tooltip closing
|
||||||
expect(mock$).toHaveBeenLastCalledWith('.copy-room');
|
await act(() => vi.runAllTimers());
|
||||||
expect(mockTooltip).toHaveBeenLastCalledWith('hide');
|
|
||||||
|
expect(queryByText('Copied')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -21,10 +21,8 @@ exports[`RoomLink > should display 1`] = `
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="copy-room btn btn-secondary"
|
class="copy-room btn btn-secondary"
|
||||||
data-clipboard-text="http://localhost:3000/roomId"
|
data-testid="copy-room-button"
|
||||||
data-placement="bottom"
|
id="copy-room"
|
||||||
data-toggle="tooltip"
|
|
||||||
title="copyButton"
|
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
@ -1,33 +1,31 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Copy } from 'react-feather';
|
import { Copy } from 'react-feather';
|
||||||
import Clipboard from 'clipboard';
|
import { Tooltip } from 'react-tooltip';
|
||||||
import $ from 'jquery';
|
|
||||||
|
|
||||||
const RoomLink = ({ roomId, translations }) => {
|
const RoomLink = ({ roomId, translations }) => {
|
||||||
|
const [showTooltip, setShowTooltip] = React.useState(false);
|
||||||
|
const mountedRef = React.useRef(true);
|
||||||
|
|
||||||
const roomUrl = `${window.location.origin}/${roomId}`;
|
const roomUrl = `${window.location.origin}/${roomId}`;
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const clip = new Clipboard('.copy-room');
|
mountedRef.current = true;
|
||||||
|
|
||||||
clip.on('success', () => {
|
|
||||||
$('.copy-room').tooltip('show');
|
|
||||||
setTimeout(() => {
|
|
||||||
$('.copy-room').tooltip('hide');
|
|
||||||
}, 3000);
|
|
||||||
});
|
|
||||||
|
|
||||||
$(() => {
|
|
||||||
$('.copy-room').tooltip({
|
|
||||||
trigger: 'manual',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if ($('.copy-room').tooltip) $('.copy-room').tooltip('hide');
|
mountedRef.current = false;
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleClick = async () => {
|
||||||
|
await navigator.clipboard.writeText(roomUrl);
|
||||||
|
setShowTooltip(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
if (mountedRef.current) {
|
||||||
|
setShowTooltip(false);
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form>
|
<form>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
@ -35,16 +33,24 @@ const RoomLink = ({ roomId, translations }) => {
|
|||||||
<input id="room-url" className="form-control" type="text" readOnly value={roomUrl} />
|
<input id="room-url" className="form-control" type="text" readOnly value={roomUrl} />
|
||||||
<div className="input-group-append">
|
<div className="input-group-append">
|
||||||
<button
|
<button
|
||||||
|
id="copy-room"
|
||||||
|
data-testid="copy-room-button"
|
||||||
className="copy-room btn btn-secondary"
|
className="copy-room btn btn-secondary"
|
||||||
type="button"
|
type="button"
|
||||||
data-toggle="tooltip"
|
onClick={handleClick}
|
||||||
data-placement="bottom"
|
|
||||||
data-clipboard-text={roomUrl}
|
|
||||||
title={translations.copyButtonTooltip}
|
|
||||||
>
|
>
|
||||||
<Copy />
|
<Copy />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{showTooltip && (
|
||||||
|
<Tooltip
|
||||||
|
anchorId="copy-room"
|
||||||
|
content={translations.copyButtonTooltip}
|
||||||
|
place="top"
|
||||||
|
events={[]}
|
||||||
|
isOpen={true}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -8,6 +8,7 @@ import 'bootstrap/dist/css/bootstrap.min.css';
|
|||||||
import 'react-simple-dropdown/styles/Dropdown.css';
|
import 'react-simple-dropdown/styles/Dropdown.css';
|
||||||
import './stylesheets/app.sass';
|
import './stylesheets/app.sass';
|
||||||
import 'bootstrap/dist/js/bootstrap.bundle.min.js';
|
import 'bootstrap/dist/js/bootstrap.bundle.min.js';
|
||||||
|
import 'react-tooltip/dist/react-tooltip.css';
|
||||||
|
|
||||||
import configureStore from '@/store/';
|
import configureStore from '@/store/';
|
||||||
import Home from '@/components/Home/';
|
import Home from '@/components/Home/';
|
||||||
|
@ -345,6 +345,18 @@
|
|||||||
minimatch "^3.1.2"
|
minimatch "^3.1.2"
|
||||||
strip-json-comments "^3.1.1"
|
strip-json-comments "^3.1.1"
|
||||||
|
|
||||||
|
"@floating-ui/core@^1.0.4":
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.0.4.tgz#03066eaea8e9b2a2cd3f5aaa60f1e0f580ebe88e"
|
||||||
|
integrity sha512-FPFLbg2b06MIw1dqk2SOEMAMX3xlrreGjcui5OTxfBDtaKTmh0kioOVjT8gcfl58juawL/yF+S+gnq8aUYQx/Q==
|
||||||
|
|
||||||
|
"@floating-ui/dom@^1.0.4":
|
||||||
|
version "1.0.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.0.12.tgz#07c870a05d9b825a6d7657524f48fe6761722800"
|
||||||
|
integrity sha512-HeG/wHoa2laUHlDX3xkzqlUqliAfa+zqV04LaKIwNCmCNaW2p0fQi4/Kd0LB4GdFoJ2UllLFq5gWnXAd67lg7w==
|
||||||
|
dependencies:
|
||||||
|
"@floating-ui/core" "^1.0.4"
|
||||||
|
|
||||||
"@humanwhocodes/config-array@^0.11.6":
|
"@humanwhocodes/config-array@^0.11.6":
|
||||||
version "0.11.7"
|
version "0.11.7"
|
||||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.7.tgz#38aec044c6c828f6ed51d5d7ae3d9b9faf6dbb0f"
|
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.7.tgz#38aec044c6c828f6ed51d5d7ae3d9b9faf6dbb0f"
|
||||||
@ -1083,15 +1095,6 @@ classnames@^2.1.2, classnames@^2.3.2:
|
|||||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
|
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
|
||||||
integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
|
integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
|
||||||
|
|
||||||
clipboard@^2.0.11:
|
|
||||||
version "2.0.11"
|
|
||||||
resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.11.tgz#62180360b97dd668b6b3a84ec226975762a70be5"
|
|
||||||
integrity sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==
|
|
||||||
dependencies:
|
|
||||||
good-listener "^1.2.2"
|
|
||||||
select "^1.1.2"
|
|
||||||
tiny-emitter "^2.0.0"
|
|
||||||
|
|
||||||
color-convert@^1.9.0:
|
color-convert@^1.9.0:
|
||||||
version "1.9.3"
|
version "1.9.3"
|
||||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||||
@ -1248,11 +1251,6 @@ delayed-stream@~1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||||
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
|
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
|
||||||
|
|
||||||
delegate@^3.1.2:
|
|
||||||
version "3.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166"
|
|
||||||
integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==
|
|
||||||
|
|
||||||
diff-sequences@^29.3.1:
|
diff-sequences@^29.3.1:
|
||||||
version "29.3.1"
|
version "29.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.3.1.tgz#104b5b95fe725932421a9c6e5b4bef84c3f2249e"
|
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.3.1.tgz#104b5b95fe725932421a9c6e5b4bef84c3f2249e"
|
||||||
@ -1846,13 +1844,6 @@ globby@^11.1.0:
|
|||||||
merge2 "^1.4.1"
|
merge2 "^1.4.1"
|
||||||
slash "^3.0.0"
|
slash "^3.0.0"
|
||||||
|
|
||||||
good-listener@^1.2.2:
|
|
||||||
version "1.2.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
|
|
||||||
integrity sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==
|
|
||||||
dependencies:
|
|
||||||
delegate "^3.1.2"
|
|
||||||
|
|
||||||
gopd@^1.0.1:
|
gopd@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
|
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
|
||||||
@ -2962,6 +2953,14 @@ react-simple-dropdown@^3.2.3:
|
|||||||
classnames "^2.1.2"
|
classnames "^2.1.2"
|
||||||
prop-types "^15.5.8"
|
prop-types "^15.5.8"
|
||||||
|
|
||||||
|
react-tooltip@^5.2.0:
|
||||||
|
version "5.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-5.2.0.tgz#e10e7de2385e8fe6bf3438739c574558b455de3b"
|
||||||
|
integrity sha512-EH6XIg2MDbMTEElSAZQVXMVeFoOhTgQuea2or0iwyzsr9v8rJf3ImMhOtq7Xe/BPlougxC+PmOibazodLdaRoA==
|
||||||
|
dependencies:
|
||||||
|
"@floating-ui/dom" "^1.0.4"
|
||||||
|
classnames "^2.3.2"
|
||||||
|
|
||||||
react@^18.2.0:
|
react@^18.2.0:
|
||||||
version "18.2.0"
|
version "18.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
|
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
|
||||||
@ -3118,11 +3117,6 @@ scheduler@^0.23.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
|
|
||||||
select@^1.1.2:
|
|
||||||
version "1.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
|
|
||||||
integrity sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==
|
|
||||||
|
|
||||||
semver@^6.0.0, semver@^6.3.0:
|
semver@^6.0.0, semver@^6.3.0:
|
||||||
version "6.3.0"
|
version "6.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||||
@ -3299,11 +3293,6 @@ text-table@^0.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
||||||
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
|
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
|
||||||
|
|
||||||
tiny-emitter@^2.0.0:
|
|
||||||
version "2.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
|
|
||||||
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
|
|
||||||
|
|
||||||
tinybench@^2.3.1:
|
tinybench@^2.3.1:
|
||||||
version "2.3.1"
|
version "2.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.3.1.tgz#14f64e6b77d7ef0b1f6ab850c7a808c6760b414d"
|
resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.3.1.tgz#14f64e6b77d7ef0b1f6ab850c7a808c6760b414d"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user