From 6a1f89f9a67b947f56fb2b0f446001630292ea85 Mon Sep 17 00:00:00 2001 From: Pawel Skroban Date: Mon, 20 Jan 2025 17:50:50 +0100 Subject: [PATCH 1/3] Add copy text component --- src/components/index.ts | 1 + .../__snapshots__/badge.spec.tsx.snap | 9 +++ .../ui/copy-text/copy-text.spec.tsx | 13 ++++ src/components/ui/copy-text/copy-text.tsx | 69 +++++++++++++++++++ src/components/ui/copy-text/index.ts | 1 + src/components/ui/icon/types.ts | 1 + src/stories/CopyText.stories.tsx | 19 +++++ style.css | 49 ++++++++++--- 8 files changed, 153 insertions(+), 9 deletions(-) create mode 100644 src/components/ui/copy-text/__snapshots__/badge.spec.tsx.snap create mode 100644 src/components/ui/copy-text/copy-text.spec.tsx create mode 100644 src/components/ui/copy-text/copy-text.tsx create mode 100644 src/components/ui/copy-text/index.ts create mode 100644 src/stories/CopyText.stories.tsx diff --git a/src/components/index.ts b/src/components/index.ts index 1e0b92d..80d72a9 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -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'; diff --git a/src/components/ui/copy-text/__snapshots__/badge.spec.tsx.snap b/src/components/ui/copy-text/__snapshots__/badge.spec.tsx.snap new file mode 100644 index 0000000..3bb6a19 --- /dev/null +++ b/src/components/ui/copy-text/__snapshots__/badge.spec.tsx.snap @@ -0,0 +1,9 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Badge > match snapshot 1`] = ` +
+
+
+`; diff --git a/src/components/ui/copy-text/copy-text.spec.tsx b/src/components/ui/copy-text/copy-text.spec.tsx new file mode 100644 index 0000000..08bffa5 --- /dev/null +++ b/src/components/ui/copy-text/copy-text.spec.tsx @@ -0,0 +1,13 @@ +import { describe, expect, it } from 'vitest'; + +import { Badge } from './badge'; +import React from 'react'; +import { render } from '@testing-library/react'; + +describe('Badge', () => { + it('match snapshot', () => { + const { container } = render(); + + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/components/ui/copy-text/copy-text.tsx b/src/components/ui/copy-text/copy-text.tsx new file mode 100644 index 0000000..207a765 --- /dev/null +++ b/src/components/ui/copy-text/copy-text.tsx @@ -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 = ({ + 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 ( + setCopied(true)} + > + {children} + + + ); +}; diff --git a/src/components/ui/copy-text/index.ts b/src/components/ui/copy-text/index.ts new file mode 100644 index 0000000..5760b39 --- /dev/null +++ b/src/components/ui/copy-text/index.ts @@ -0,0 +1 @@ +export { CopyText } from './copy-text'; diff --git a/src/components/ui/icon/types.ts b/src/components/ui/icon/types.ts index 00ad0be..a15cf9e 100644 --- a/src/components/ui/icon/types.ts +++ b/src/components/ui/icon/types.ts @@ -261,6 +261,7 @@ export const ICONS = { }; export enum IconSize { + xxs = 12, xs = 16, s = 20, m = 24, diff --git a/src/stories/CopyText.stories.tsx b/src/stories/CopyText.stories.tsx new file mode 100644 index 0000000..ab8a873 --- /dev/null +++ b/src/stories/CopyText.stories.tsx @@ -0,0 +1,19 @@ +import { CopyText } from '@/components/ui/copy-text'; +import type { Meta, StoryObj } from '@storybook/react'; + +const meta = { + component: CopyText +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Example = { + args: { + text: 'Copied text from clipboard', + children: Hover to copy with click!, + position: 'right' + }, + render: (args) => +} satisfies Story; diff --git a/style.css b/style.css index aa3c11b..8f56811 100644 --- a/style.css +++ b/style.css @@ -4646,6 +4646,10 @@ input.tab:checked + .tab-content, fill: #6b7280; } +.fill-green-600{ + fill: #16a34a; +} + .fill-red-500{ fill: #ef4444; } @@ -4707,6 +4711,11 @@ input.tab:checked + .tab-content, padding-right: 0.25rem; } +.px-2{ + padding-left: 0.5rem; + padding-right: 0.5rem; +} + .px-3{ padding-left: 0.75rem; padding-right: 0.75rem; @@ -4732,6 +4741,11 @@ input.tab:checked + .tab-content, padding-bottom: 0px; } +.py-0\.5{ + padding-top: 0.125rem; + padding-bottom: 0.125rem; +} + .py-2{ padding-top: 0.5rem; padding-bottom: 0.5rem; @@ -4908,11 +4922,21 @@ input.tab:checked + .tab-content, color: rgb(107 114 128 / var(--tw-text-opacity, 1)); } +.text-gray-800{ + --tw-text-opacity: 1; + color: rgb(31 41 55 / var(--tw-text-opacity, 1)); +} + .text-gray-900{ --tw-text-opacity: 1; color: rgb(17 24 39 / var(--tw-text-opacity, 1)); } +.text-green-600{ + --tw-text-opacity: 1; + color: rgb(22 163 74 / var(--tw-text-opacity, 1)); +} + .text-green-800{ --tw-text-opacity: 1; color: rgb(22 101 52 / var(--tw-text-opacity, 1)); @@ -5021,6 +5045,10 @@ input.tab:checked + .tab-content, opacity: 1; } +.opacity-95{ + opacity: 0.95; +} + .\!shadow{ --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1) !important; --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color) !important; @@ -5238,6 +5266,10 @@ input[type="checkbox"].green:checked { background-color: rgb(29 78 216 / var(--tw-bg-opacity, 1)); } +.hover\:flex:hover{ + display: flex; +} + .hover\:border-blue-100:hover{ --tw-border-opacity: 1; border-color: rgb(219 234 254 / var(--tw-border-opacity, 1)); @@ -5296,24 +5328,19 @@ input[type="checkbox"].green:checked { font-weight: 600; } -.hover\:text-blue-500:hover{ - --tw-text-opacity: 1; - color: rgb(59 130 246 / var(--tw-text-opacity, 1)); -} - .hover\:text-blue-700:hover{ --tw-text-opacity: 1; color: rgb(29 78 216 / var(--tw-text-opacity, 1)); } -.hover\:text-slate-700:hover{ +.hover\:text-blue-800:hover{ --tw-text-opacity: 1; - color: rgb(51 65 85 / var(--tw-text-opacity, 1)); + color: rgb(30 64 175 / var(--tw-text-opacity, 1)); } -.hover\:text-blue-800:hover{ +.hover\:text-slate-700:hover{ --tw-text-opacity: 1; - color: rgb(30 64 175 / var(--tw-text-opacity, 1)); + color: rgb(51 65 85 / var(--tw-text-opacity, 1)); } .focus\:border:focus{ @@ -5470,6 +5497,10 @@ input[type="checkbox"].green:checked { opacity: 0.5; } +.group\/copy-text:hover .group-hover\/copy-text\:flex{ + display: flex; +} + .group:hover .group-hover\:fill-blue-700{ fill: #1d4ed8; } From 50e7129375f6e648c9443cfc517257c1cf9e865f Mon Sep 17 00:00:00 2001 From: Pawel Skroban Date: Mon, 20 Jan 2025 17:54:47 +0100 Subject: [PATCH 2/3] copy-text specs --- .../__snapshots__/badge.spec.tsx.snap | 9 -- .../__snapshots__/copy-text.spec.tsx.snap | 125 ++++++++++++++++++ .../ui/copy-text/copy-text.spec.tsx | 35 ++++- .../DateRangeSelect.spec.tsx | 2 - 4 files changed, 157 insertions(+), 14 deletions(-) delete mode 100644 src/components/ui/copy-text/__snapshots__/badge.spec.tsx.snap create mode 100644 src/components/ui/copy-text/__snapshots__/copy-text.spec.tsx.snap diff --git a/src/components/ui/copy-text/__snapshots__/badge.spec.tsx.snap b/src/components/ui/copy-text/__snapshots__/badge.spec.tsx.snap deleted file mode 100644 index 3bb6a19..0000000 --- a/src/components/ui/copy-text/__snapshots__/badge.spec.tsx.snap +++ /dev/null @@ -1,9 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`Badge > match snapshot 1`] = ` -
-
-
-`; diff --git a/src/components/ui/copy-text/__snapshots__/copy-text.spec.tsx.snap b/src/components/ui/copy-text/__snapshots__/copy-text.spec.tsx.snap new file mode 100644 index 0000000..0d05711 --- /dev/null +++ b/src/components/ui/copy-text/__snapshots__/copy-text.spec.tsx.snap @@ -0,0 +1,125 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Badge > match snapshot 1`] = ` +
+ + Hover to copy + + +
+`; + +exports[`CopyText > match snapshot 1`] = ` +
+ + Hover to copy + + +
+`; + +exports[`CopyText > renders correctly with position bottom 1`] = ` +
+ + Hover to copy + + +
+`; + +exports[`CopyText > renders correctly with position overlay 1`] = ` +
+ + Hover to copy + + +
+`; diff --git a/src/components/ui/copy-text/copy-text.spec.tsx b/src/components/ui/copy-text/copy-text.spec.tsx index 08bffa5..cac36e5 100644 --- a/src/components/ui/copy-text/copy-text.spec.tsx +++ b/src/components/ui/copy-text/copy-text.spec.tsx @@ -1,13 +1,42 @@ import { describe, expect, it } from 'vitest'; -import { Badge } from './badge'; +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( + + Hover to copy + + ); -describe('Badge', () => { it('match snapshot', () => { - const { container } = render(); + 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(); }); }); diff --git a/src/components/ui/date-range-select/DateRangeSelect.spec.tsx b/src/components/ui/date-range-select/DateRangeSelect.spec.tsx index 0566409..0a8a292 100644 --- a/src/components/ui/date-range-select/DateRangeSelect.spec.tsx +++ b/src/components/ui/date-range-select/DateRangeSelect.spec.tsx @@ -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', () => { @@ -18,7 +17,6 @@ describe('DateRangeSelect', () => { }); it('should render with values from props', () => { - console.log(addDays(new Date(), -7).toISOString()); const { getByDisplayValue } = render( {}} From 49f547d0b9427f2ab30e5210c0dcb622f42a55b7 Mon Sep 17 00:00:00 2001 From: Pawel Skroban Date: Mon, 20 Jan 2025 17:55:05 +0100 Subject: [PATCH 3/3] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6de5cbc..a35e2fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@awell-health/design-system", - "version": "0.12.37", + "version": "0.12.38", "type": "module", "files": [ "dist"