Skip to content

Commit

Permalink
Merge pull request #743 from JiscSD/OC-977
Browse files Browse the repository at this point in the history
OC-977: Button to copy DOI
  • Loading branch information
finlay-jisc authored Dec 18, 2024
2 parents 06c5714 + 4ba450b commit e72c73f
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 30 deletions.
1 change: 0 additions & 1 deletion e2e/tests/PageModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ export const PageModel = {
// coi
'h2:has-text("Conflict of interest")'
],
doiLink: 'aside [aria-label="DOI link: https://handle.test.datacite.org/10.82259/cl3fz14dr0001es6i5ji51rq4"]',
authorLink: 'a[href="/authors/octopus"]:has-text("Octopus")',
signInForMoreButton: 'text=Sign in for more actions',
verifyEmailForMoreButton: 'text=Verify your email for more actions',
Expand Down
4 changes: 2 additions & 2 deletions e2e/tests/helpers/livePublication.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BrowserContext, expect, Page } from '@playwright/test';
import { expect, Page } from '@playwright/test';
import { PageModel } from '../PageModel';

export const checkLivePublicationLayout = async (page: Page, id: string, loggedIn?: boolean) => {
Expand All @@ -14,7 +14,7 @@ export const checkLivePublicationLayout = async (page: Page, id: string, loggedI
}

// Expect DOI link
await expect(page.locator(PageModel.livePublication.doiLink)).toHaveAttribute(
await expect(page.getByRole('link', { name: 'DOI' })).toHaveAttribute(
'href',
`https://handle.test.datacite.org/10.82259/${id}`
);
Expand Down
35 changes: 35 additions & 0 deletions ui/src/__tests__/components/CopyButton.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as Components from '@/components';

import { render, screen } from '@testing-library/react';
import { userEvent } from '@testing-library/user-event';

Object.assign(navigator, {
clipboard: {
writeText: jest.fn(() => Promise.resolve())
}
});

describe('CopyButton', () => {
const buttonTitle = 'Copy some text';
const textToCopy = 'Test value';

beforeEach(() => {
render(<Components.CopyButton textToCopy={textToCopy} title={buttonTitle} />);
});

it('Button is present', () => {
expect(screen.getByRole('button', { name: buttonTitle })).toBeInTheDocument();
});

it('Clicking the button copies the text to the clipboard', async () => {
await userEvent.click(screen.getByRole('button', { name: buttonTitle }));
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(textToCopy);
});

it('Clicking the button updates the aria-live message', async () => {
expect(screen.queryByText('Copied!')).not.toBeInTheDocument();
await userEvent.click(screen.getByRole('button', { name: buttonTitle }));
expect(screen.getByText('Copied!')).toBeInTheDocument();
expect(screen.getByText('Copied!')).toHaveAttribute('aria-live', 'polite');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ describe('Basic tests', () => {
);
});
it('Shows DOI link', () => {
expect(screen.getByText('DOI:')).toBeInTheDocument();
expect(screen.getByText('DOI')).toBeInTheDocument();
expect(
screen.getByRole('link', {
name: `DOI link: ${versionlessDoiUrl}`
name: 'DOI'
})
).toHaveAttribute('href', `${versionlessDoiUrl}`);
});
Expand Down Expand Up @@ -93,18 +93,18 @@ describe('Multi-version publication with Peer Reviews, Flags and version DOI', (
);
});
it('Shows version DOI link', () => {
expect(screen.getByText('DOI (This Version):')).toBeInTheDocument();
expect(screen.getByText('DOI (This Version)')).toBeInTheDocument();
expect(
screen.getByRole('link', {
name: `DOI link: ${versionDoiUrl}`
name: 'DOI (This Version)'
})
).toHaveAttribute('href', versionDoiUrl);
});
it('Shows "versionless" DOI link', () => {
expect(screen.getByText('DOI (All Versions):')).toBeInTheDocument();
expect(screen.getByText('DOI (All Versions)')).toBeInTheDocument();
expect(
screen.getByRole('link', {
name: `DOI link: ${versionlessDoiUrl}`
name: 'DOI (All Versions)'
})
).toHaveAttribute('href', versionlessDoiUrl);
});
Expand Down
48 changes: 48 additions & 0 deletions ui/src/components/CopyButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { useState } from 'react';
import * as OutlineIcons from '@heroicons/react/24/outline';

import * as Components from '@/components';
import * as Stores from '@/stores';

type Props = {
id?: string;
className?: string;
textToCopy: string;
title: string;
};

const CopyButton: React.FC<Props> = (props): React.ReactElement => {
const [copied, setCopied] = useState(false);
const setToast = Stores.useToastStore((state) => state.setToast);

const confirmationMessage = 'Copied!';

const handleCopy = () => {
navigator.clipboard.writeText(props.textToCopy).then(() => {
setCopied(true);
setToast({
visible: true,
title: confirmationMessage,
message: null,
icon: <OutlineIcons.DocumentDuplicateIcon className="h-6 w-6 text-teal-400" />,
dismiss: false
});
});
};

return (
<>
<span className="sr-only" aria-live="polite">
{copied && confirmationMessage}
</span>
<Components.IconButton
className={props.className}
icon={<OutlineIcons.DocumentDuplicateIcon className="h-4 w-4" />}
title={props.title}
onClick={handleCopy}
/>
</>
);
};

export default CopyButton;
47 changes: 28 additions & 19 deletions ui/src/components/Publication/SidebarCard/General/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,33 +86,42 @@ const General: React.FC<Props> = (props): React.ReactElement => {
{showVersionDoi && (
<div className="flex w-full flex-wrap whitespace-normal">
<span className="mr-2 whitespace-nowrap text-sm font-semibold text-grey-800 transition-colors duration-500 dark:text-grey-100">
DOI (This Version):
<span id="version-doi-label">DOI (This Version)</span>:
</span>
<span className="flex gap-4">
<Components.Link
href={versionDoiUrl}
aria-labelledby="version-doi-label"
className="flex items-center text-sm font-medium text-teal-600 transition-colors duration-500 hover:underline dark:text-teal-400"
openNew={true}
>
<p className="break-words break-all">{versionDoiUrl}</p>
<OutlineIcons.ArrowTopRightOnSquareIcon className="ml-1 h-4 w-4 flex-shrink-0" />
</Components.Link>
<Components.CopyButton textToCopy={versionDoiUrl} title="Copy this version's DOI" />
</span>
</div>
)}

<div className={`flex w-full ${props.publicationVersion.doi ? 'flex-wrap' : ''} whitespace-normal`}>
<span className="mr-2 whitespace-nowrap text-sm font-semibold text-grey-800 transition-colors duration-500 dark:text-grey-100">
<span id="versionless-doi-label">{showVersionDoi ? 'DOI (All Versions)' : 'DOI'}</span>:
</span>
<span className="flex gap-4">
<Components.Link
href={versionDoiUrl}
ariaLabel={`DOI link: ${versionDoiUrl}`}
href={versionlessDoiUrl}
aria-labelledby="versionless-doi-label"
className="flex items-center text-sm font-medium text-teal-600 transition-colors duration-500 hover:underline dark:text-teal-400"
openNew={true}
>
<p className="break-words break-all">{versionDoiUrl}</p>
<p className="break-words break-all">{versionlessDoiUrl}</p>
<OutlineIcons.ArrowTopRightOnSquareIcon className="ml-1 h-4 w-4 flex-shrink-0" />
</Components.Link>
</div>
)}

<div className={`flex w-full ${props.publicationVersion.doi ? 'flex-wrap' : ''} whitespace-normal`}>
<span className="mr-2 whitespace-nowrap text-sm font-semibold text-grey-800 transition-colors duration-500 dark:text-grey-100">
{showVersionDoi ? 'DOI (All Versions):' : 'DOI:'}
<Components.CopyButton
textToCopy={versionlessDoiUrl}
title={`Copy ${showVersionDoi && 'all versions'} DOI`}
/>
</span>
<Components.Link
href={versionlessDoiUrl}
ariaLabel={`DOI link: ${versionlessDoiUrl}`}
className="flex items-center text-sm font-medium text-teal-600 transition-colors duration-500 hover:underline dark:text-teal-400"
openNew={true}
>
<p className="break-words break-all">{versionlessDoiUrl}</p>
<OutlineIcons.ArrowTopRightOnSquareIcon className="ml-1 h-4 w-4 flex-shrink-0" />
</Components.Link>
</div>

{props.publicationVersion.publication.type !== 'PEER_REVIEW' && (
Expand Down
1 change: 1 addition & 0 deletions ui/src/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export { default as BookmarkedPublications } from './Publication/BookmarkedPubli
export { default as BookmarkedTopics } from './Topic/BookmarkedTopics';
export { default as Button } from './Button';
export { default as ContentSection } from './GenericContent/ContentSection';
export { default as CopyButton } from './CopyButton';
export { default as EditAffiliationsModal } from './EditAffiliationsModal';
export { default as EditReferenceModal } from './References/EditReferenceModal';
export { default as EnableDarkMode } from './EnableDarkMode';
Expand Down
4 changes: 2 additions & 2 deletions ui/src/stores/toast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ let store: any = (set: any): Types.ToastStoreType => ({

if (process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF === 'local') store = zustandMiddleware.devtools(store);

const useNoficiationStore = create<Types.ToastStoreType>(store);
const useNotificiationStore = create<Types.ToastStoreType>(store);

export default useNoficiationStore;
export default useNotificiationStore;

0 comments on commit e72c73f

Please sign in to comment.