Skip to content

Commit

Permalink
Merge pull request #182 from awell-health/copy
Browse files Browse the repository at this point in the history
Copy
  • Loading branch information
skrobek authored Jan 20, 2025
2 parents 2124b83 + 49f547d commit c4c3d28
Show file tree
Hide file tree
Showing 10 changed files with 299 additions and 12 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@awell-health/design-system",
"version": "0.12.37",
"version": "0.12.38",
"type": "module",
"files": [
"dist"
Expand Down
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export * from './ui/avatar';
export * from './ui/badge';
export * from './ui/button';
export * from './ui/card';
export * from './ui/copy-text';
export * from './ui/date-range-select';
export * from './ui/divider';
export * from './ui/drawer';
Expand Down
125 changes: 125 additions & 0 deletions src/components/ui/copy-text/__snapshots__/copy-text.spec.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`Badge > match snapshot 1`] = `
<div>
<span
class="group/copy-text relative cursor-pointer flex items-center gap-1"
>
Hover to copy
<span
class="text-xs items-center hidden group-hover/copy-text:flex hover:flex gap-1"
>
<svg
class="remixicon fill-slate-500"
fill="default"
height="12"
viewBox="0 0 24 24"
width="12"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.9998 6V3C6.9998 2.44772 7.44752 2 7.9998 2H19.9998C20.5521 2 20.9998 2.44772 20.9998 3V17C20.9998 17.5523 20.5521 18 19.9998 18H16.9998V20.9991C16.9998 21.5519 16.5499 22 15.993 22H4.00666C3.45059 22 3 21.5554 3 20.9991L3.0026 7.00087C3.0027 6.44811 3.45264 6 4.00942 6H6.9998ZM8.9998 6H16.9998V16H18.9998V4H8.9998V6Z"
/>
</svg>
<span
class="text-sm text-slate-600"
>
Click to copy
</span>
</span>
</span>
</div>
`;

exports[`CopyText > match snapshot 1`] = `
<div>
<span
class="group/copy-text relative cursor-pointer flex items-center gap-1"
>
Hover to copy
<span
class="text-xs items-center hidden group-hover/copy-text:flex hover:flex gap-1"
>
<svg
class="remixicon fill-slate-500"
fill="default"
height="12"
viewBox="0 0 24 24"
width="12"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.9998 6V3C6.9998 2.44772 7.44752 2 7.9998 2H19.9998C20.5521 2 20.9998 2.44772 20.9998 3V17C20.9998 17.5523 20.5521 18 19.9998 18H16.9998V20.9991C16.9998 21.5519 16.5499 22 15.993 22H4.00666C3.45059 22 3 21.5554 3 20.9991L3.0026 7.00087C3.0027 6.44811 3.45264 6 4.00942 6H6.9998ZM8.9998 6H16.9998V16H18.9998V4H8.9998V6Z"
/>
</svg>
<span
class="text-sm text-slate-600"
>
Click to copy
</span>
</span>
</span>
</div>
`;

exports[`CopyText > renders correctly with position bottom 1`] = `
<div>
<span
class="group/copy-text relative cursor-pointer"
>
Hover to copy
<span
class="text-xs items-center gap-1 hidden group-hover/copy-text:flex absolute"
>
<svg
class="remixicon fill-slate-500"
fill="default"
height="12"
viewBox="0 0 24 24"
width="12"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.9998 6V3C6.9998 2.44772 7.44752 2 7.9998 2H19.9998C20.5521 2 20.9998 2.44772 20.9998 3V17C20.9998 17.5523 20.5521 18 19.9998 18H16.9998V20.9991C16.9998 21.5519 16.5499 22 15.993 22H4.00666C3.45059 22 3 21.5554 3 20.9991L3.0026 7.00087C3.0027 6.44811 3.45264 6 4.00942 6H6.9998ZM8.9998 6H16.9998V16H18.9998V4H8.9998V6Z"
/>
</svg>
<span
class="text-sm text-slate-600"
>
Click to copy
</span>
</span>
</span>
</div>
`;

exports[`CopyText > renders correctly with position overlay 1`] = `
<div>
<span
class="group/copy-text relative cursor-pointer"
>
Hover to copy
<span
class="text-xs items-center gap-1 hidden group-hover/copy-text:flex absolute top-0 left-0 h-full bg-gray-200 opacity-95 rounded-md w-full px-2 py-0.5"
>
<svg
class="remixicon fill-slate-500"
fill="default"
height="12"
viewBox="0 0 24 24"
width="12"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.9998 6V3C6.9998 2.44772 7.44752 2 7.9998 2H19.9998C20.5521 2 20.9998 2.44772 20.9998 3V17C20.9998 17.5523 20.5521 18 19.9998 18H16.9998V20.9991C16.9998 21.5519 16.5499 22 15.993 22H4.00666C3.45059 22 3 21.5554 3 20.9991L3.0026 7.00087C3.0027 6.44811 3.45264 6 4.00942 6H6.9998ZM8.9998 6H16.9998V16H18.9998V4H8.9998V6Z"
/>
</svg>
<span
class="text-sm text-slate-600"
>
Click to copy
</span>
</span>
</span>
</div>
`;
42 changes: 42 additions & 0 deletions src/components/ui/copy-text/copy-text.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { describe, expect, it } from 'vitest';

import { CopyText } from './copy-text';
import React from 'react';
import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

describe('CopyText', () => {
const subject = (position: 'right' | 'bottom' | 'overlay') =>
render(
<CopyText text='Hover to copy' position={position}>
Hover to copy
</CopyText>
);

it('match snapshot', () => {
const { container } = subject('right');

expect(container).toMatchSnapshot();
});

it('copies text to clipboard on click', async () => {
const user = userEvent.setup();
const { getByText } = subject('right');

const copyTextElement = getByText('Hover to copy');
await user.click(copyTextElement);

const clipboardText = await navigator.clipboard.readText();
expect(clipboardText).toBe('Hover to copy');
});

it('renders correctly with position bottom', () => {
const { container } = subject('bottom');
expect(container).toMatchSnapshot();
});

it('renders correctly with position overlay', () => {
const { container } = subject('overlay');
expect(container).toMatchSnapshot();
});
});
69 changes: 69 additions & 0 deletions src/components/ui/copy-text/copy-text.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { FC, useEffect, useState } from 'react';
import { Icon, IconSize } from '../icon';
import React from 'react';
import { cn } from '../../../lib/utils';

interface Props {
text: string;
children: React.ReactNode | JSX.Element;
copiedText?: string;
position?: 'right' | 'bottom' | 'overlay';
className?: string;
}

export const CopyText: FC<Props> = ({
text,
children,
copiedText,
position = 'right',
className
}) => {
const [copied, setCopied] = useState(false);

useEffect(() => {
if (copied) {
navigator.clipboard.writeText(text);
const timeout = setTimeout(() => {
setCopied(false);
}, 2000);
return () => {
clearTimeout(timeout);
};
}
}, [copied]);

return (
<span
className={cn(
'group/copy-text relative cursor-pointer',
position === 'right' && 'flex items-center gap-1'
)}
onClick={() => setCopied(true)}
>
{children}
<span
className={cn(
'text-xs items-center gap-1 hidden group-hover/copy-text:flex',
position === 'right' && 'hover:flex gap-1',
position === 'bottom' && 'absolute',
position === 'overlay' &&
'absolute top-0 left-0 h-full bg-gray-200 opacity-95 rounded-md w-full px-2 py-0.5',
className
)}
>
{!copied && (
<>
<Icon icon='RiFileCopyFill' className=' fill-slate-500' size={IconSize.xxs} />
<span className='text-sm text-slate-600'>Click to copy</span>
</>
)}
{copied && (
<>
<Icon icon='RiCheckFill' className=' fill-green-600' size={IconSize.xxs} />
<span className='text-sm text-green-600'>{copiedText ?? 'Copied to clipboard'}</span>
</>
)}
</span>
</span>
);
};
1 change: 1 addition & 0 deletions src/components/ui/copy-text/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { CopyText } from './copy-text';
2 changes: 0 additions & 2 deletions src/components/ui/date-range-select/DateRangeSelect.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import * as React from 'react';
import { render } from '@testing-library/react';
import { expect, it, describe } from 'vitest';
import { DateRangeSelect } from './DateRangeSelect';
import { addDays } from 'date-fns';

describe('DateRangeSelect', () => {
it('match snapshot', () => {
Expand All @@ -18,7 +17,6 @@ describe('DateRangeSelect', () => {
});

it('should render with values from props', () => {
console.log(addDays(new Date(), -7).toISOString());
const { getByDisplayValue } = render(
<DateRangeSelect
onSelect={() => {}}
Expand Down
1 change: 1 addition & 0 deletions src/components/ui/icon/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ export const ICONS = {
};

export enum IconSize {
xxs = 12,
xs = 16,
s = 20,
m = 24,
Expand Down
19 changes: 19 additions & 0 deletions src/stories/CopyText.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { CopyText } from '@/components/ui/copy-text';
import type { Meta, StoryObj } from '@storybook/react';

const meta = {
component: CopyText
} satisfies Meta<typeof CopyText>;

export default meta;

type Story = StoryObj<typeof CopyText>;

export const Example = {
args: {
text: 'Copied text from clipboard',
children: <span className='text-gray-800'>Hover to copy with click!</span>,
position: 'right'
},
render: (args) => <CopyText {...args} />
} satisfies Story;
Loading

0 comments on commit c4c3d28

Please sign in to comment.