mirror of
https://github.com/portainer/portainer.git
synced 2025-07-19 13:29:41 +02:00
refactor(app): move react components to react codebase [EE-3179] (#6971)
This commit is contained in:
parent
212400c283
commit
18252ab854
346 changed files with 642 additions and 644 deletions
|
@ -0,0 +1,45 @@
|
|||
.fadeout {
|
||||
animation: fadeOut 2.5s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.display-text {
|
||||
opacity: 0;
|
||||
margin-left: 7px;
|
||||
color: #23ae89;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeOut {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.8;
|
||||
}
|
||||
99% {
|
||||
opacity: 0.01;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes fadeOut {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.8;
|
||||
}
|
||||
99% {
|
||||
opacity: 0.01;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import { Meta, Story } from '@storybook/react';
|
||||
import { PropsWithChildren } from 'react';
|
||||
|
||||
import { CopyButton, Props } from './CopyButton';
|
||||
|
||||
export default {
|
||||
component: CopyButton,
|
||||
title: 'Components/Buttons/CopyButton',
|
||||
} as Meta;
|
||||
|
||||
function Template({
|
||||
copyText,
|
||||
displayText,
|
||||
children,
|
||||
}: JSX.IntrinsicAttributes & PropsWithChildren<Props>) {
|
||||
return (
|
||||
<CopyButton copyText={copyText} displayText={displayText}>
|
||||
{children}
|
||||
</CopyButton>
|
||||
);
|
||||
}
|
||||
|
||||
export const Primary: Story<PropsWithChildren<Props>> = Template.bind({});
|
||||
Primary.args = {
|
||||
children: 'Copy to clipboard',
|
||||
copyText: 'this will be copied to clipboard',
|
||||
};
|
||||
|
||||
export const NoCopyText: Story<PropsWithChildren<Props>> = Template.bind({});
|
||||
NoCopyText.args = {
|
||||
children: 'Copy to clipboard without copied text',
|
||||
copyText: 'clipboard override',
|
||||
displayText: '',
|
||||
};
|
37
app/react/components/buttons/CopyButton/CopyButton.test.tsx
Normal file
37
app/react/components/buttons/CopyButton/CopyButton.test.tsx
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { fireEvent, render } from '@testing-library/react';
|
||||
|
||||
import { CopyButton } from './CopyButton';
|
||||
|
||||
test('should display a CopyButton with children', async () => {
|
||||
const children = 'test button children';
|
||||
const { findByText } = render(
|
||||
<CopyButton copyText="">{children}</CopyButton>
|
||||
);
|
||||
|
||||
const button = await findByText(children);
|
||||
expect(button).toBeTruthy();
|
||||
});
|
||||
|
||||
test('CopyButton should copy text to clipboard', async () => {
|
||||
// override navigator.clipboard.writeText (to test copy to clipboard functionality)
|
||||
let clipboardText = '';
|
||||
const writeText = jest.fn((text) => {
|
||||
clipboardText = text;
|
||||
});
|
||||
Object.assign(navigator, {
|
||||
clipboard: { writeText },
|
||||
});
|
||||
|
||||
const children = 'button';
|
||||
const copyText = 'text successfully copied to clipboard';
|
||||
const { findByText } = render(
|
||||
<CopyButton copyText={copyText}>{children}</CopyButton>
|
||||
);
|
||||
|
||||
const button = await findByText(children);
|
||||
expect(button).toBeTruthy();
|
||||
|
||||
fireEvent.click(button);
|
||||
expect(clipboardText).toBe(copyText);
|
||||
expect(writeText).toHaveBeenCalled();
|
||||
});
|
49
app/react/components/buttons/CopyButton/CopyButton.tsx
Normal file
49
app/react/components/buttons/CopyButton/CopyButton.tsx
Normal file
|
@ -0,0 +1,49 @@
|
|||
import { PropsWithChildren } from 'react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { Button } from '../Button';
|
||||
|
||||
import styles from './CopyButton.module.css';
|
||||
import { useCopy } from './useCopy';
|
||||
|
||||
export interface Props {
|
||||
copyText: string;
|
||||
fadeDelay?: number;
|
||||
displayText?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function CopyButton({
|
||||
copyText,
|
||||
fadeDelay = 1000,
|
||||
displayText = 'copied',
|
||||
className,
|
||||
children,
|
||||
}: PropsWithChildren<Props>) {
|
||||
const { handleCopy, copiedSuccessfully } = useCopy(copyText, fadeDelay);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Button
|
||||
className={className}
|
||||
size="small"
|
||||
onClick={handleCopy}
|
||||
title="Copy Value"
|
||||
type="button"
|
||||
>
|
||||
<i className="fa fa-copy space-right" aria-hidden="true" /> {children}
|
||||
</Button>
|
||||
|
||||
<span
|
||||
className={clsx(
|
||||
copiedSuccessfully && styles.fadeout,
|
||||
styles.displayText,
|
||||
'space-left'
|
||||
)}
|
||||
>
|
||||
<i className="fa fa-check" aria-hidden="true" />
|
||||
{displayText && <span className="space-left">{displayText}</span>}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
1
app/react/components/buttons/CopyButton/index.ts
Normal file
1
app/react/components/buttons/CopyButton/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export { CopyButton } from './CopyButton';
|
36
app/react/components/buttons/CopyButton/useCopy.ts
Normal file
36
app/react/components/buttons/CopyButton/useCopy.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
|
||||
export function useCopy(copyText: string, fadeDelay = 1000) {
|
||||
const [copiedSuccessfully, setCopiedSuccessfully] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const fadeoutTime = setTimeout(
|
||||
() => setCopiedSuccessfully(false),
|
||||
fadeDelay
|
||||
);
|
||||
// clear timeout when component unmounts
|
||||
return () => {
|
||||
clearTimeout(fadeoutTime);
|
||||
};
|
||||
}, [copiedSuccessfully, fadeDelay]);
|
||||
|
||||
function handleCopy() {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Clipboard
|
||||
// https://caniuse.com/?search=clipboard
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(copyText);
|
||||
} else {
|
||||
// https://stackoverflow.com/a/57192718
|
||||
const inputEl = document.createElement('textarea');
|
||||
inputEl.value = copyText;
|
||||
document.body.appendChild(inputEl);
|
||||
inputEl.select();
|
||||
document.execCommand('copy');
|
||||
inputEl.hidden = true;
|
||||
document.body.removeChild(inputEl);
|
||||
}
|
||||
setCopiedSuccessfully(true);
|
||||
}
|
||||
|
||||
return { handleCopy, copiedSuccessfully };
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue