@chayns-components/core 5.5.28 → 5.5.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -1831,6 +1831,7 @@ import { CopyableContent } from '@chayns-components/core';
1831
1831
  | `collapsedHeight` | `number \| undefined` | no | The height of the content in its collapsed state. |
1832
1832
  | `content` | `string` | yes | Markdown source used for rendering and clipboard data. |
1833
1833
  | `copyFailedMessage` | `string \| undefined` | no | Replaces the localized error message shown when copying fails. |
1834
+ | `transformClipboardHtml` | `((html: string) => string) \| undefined` | no | Transforms the generated HTML before it is written to the clipboard. |
1834
1835
 
1835
1836
  ### Types
1836
1837
 
@@ -31,7 +31,8 @@ const CopyableContent = ({
31
31
  content,
32
32
  children,
33
33
  copyFailedMessage,
34
- collapsedHeight
34
+ collapsedHeight,
35
+ transformClipboardHtml
35
36
  }) => {
36
37
  const rootRef = (0, _react.useRef)(null);
37
38
  const actionGroupRef = (0, _react.useRef)(null);
@@ -62,7 +63,7 @@ const CopyableContent = ({
62
63
  }, []);
63
64
  const handleCopy = (0, _react.useCallback)(async () => {
64
65
  try {
65
- await (0, _copyableContentClipboard.copyableContentToClipboard)(content);
66
+ await (0, _copyableContentClipboard.copyableContentToClipboard)(content, transformClipboardHtml);
66
67
  showCopyFeedback();
67
68
  } catch {
68
69
  void (0, _chaynsApi.createDialog)({
@@ -72,7 +73,7 @@ const CopyableContent = ({
72
73
  type: _chaynsApi.DialogType.TOAST
73
74
  }).open();
74
75
  }
75
- }, [content, defaultCopyFailedMessage, copyFailedMessage, showCopyFeedback]);
76
+ }, [content, defaultCopyFailedMessage, copyFailedMessage, showCopyFeedback, transformClipboardHtml]);
76
77
  return /*#__PURE__*/_react.default.createElement(_CopyableContent.StyledCopyableContent, {
77
78
  $appearance: appearance,
78
79
  $colorMode: colorMode,
@@ -1 +1 @@
1
- {"version":3,"file":"CopyableContent.js","names":["_format","require","_textstring","_chaynsApi","_react","_interopRequireWildcard","_textStrings","_interopRequireDefault","_useStickyActionState","_ColorSchemeProvider","_SharingContextMenu","_Icon","_Truncation","_CopyableContent","_CopyableContent2","_copyableContentClipboard","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","COPY_FEEDBACK_DURATION","CopyableContent","appearance","CopyableContentAppearance","Default","content","children","copyFailedMessage","collapsedHeight","rootRef","useRef","actionGroupRef","copyFeedbackTimeoutRef","isActionGroupSticky","useStickyActionState","hasCopied","setHasCopied","useState","colorScheme","useColorScheme","colorMode","theme","CopyableContentColorMode","Dark","Light","defaultCopyButtonText","useTextstringValue","textstring","ttsToITextString","textStrings","components","copyableContent","copy","defaultCopyFailedMessage","copyFailed","shareText","share","html","useMemo","formatStringToHtml","useEffect","window","clearTimeout","current","showCopyFeedback","useCallback","setTimeout","handleCopy","copyableContentToClipboard","createDialog","showCloseIcon","text","toastType","ToastType","ERROR","type","DialogType","TOAST","open","createElement","StyledCopyableContent","$appearance","$colorMode","className","ref","StyledCopyableContentActions","StyledCopyableContentActionGroup","StyledCopyableContentButton","$isSticky","onClick","icons","link","shouldShowCallingCodeAction","shouldShowCopyAction","shouldUseDefaultTriggerStyles","StyledCopyableContentTruncation","StyledCopyableContentBody","$shouldShowPadding","dangerouslySetInnerHTML","__html","displayName","_default","exports"],"sources":["../../../../src/components/copyable-content/CopyableContent.tsx"],"sourcesContent":["import { formatStringToHtml } from '@chayns-components/format';\nimport { ttsToITextString, useTextstringValue } from '@chayns-components/textstring';\nimport { createDialog, DialogType, ToastType } from 'chayns-api';\nimport React, { FC, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport textStrings from '../../constants/textStrings';\nimport { useStickyActionState } from '../../hooks/useStickyActionState';\nimport { useColorScheme } from '../color-scheme-provider/ColorSchemeProvider';\nimport SharingContextMenu from '../sharing-context-menu/SharingContextMenu';\nimport Icon from '../icon/Icon';\nimport Truncation from '../truncation/Truncation';\nimport {\n CopyableContentColorMode,\n StyledCopyableContent,\n StyledCopyableContentActionGroup,\n StyledCopyableContentActions,\n StyledCopyableContentBody,\n StyledCopyableContentButton,\n StyledCopyableContentTruncation,\n} from './CopyableContent.styles';\nimport { CopyableContentAppearance } from './CopyableContent.types';\nimport { copyableContentToClipboard } from './copyableContentClipboard';\n\nconst COPY_FEEDBACK_DURATION = 1500;\n\nexport type CopyableContentProps = {\n /**\n * Controls the visual surface of the content block.\n */\n appearance?: CopyableContentAppearance;\n /**\n * Markdown source used for rendering and clipboard data.\n */\n content: string;\n /**\n * Replaces only the visible rendered content and never the copied source.\n */\n children?: ReactNode;\n /**\n * Replaces the localized error message shown when copying fails.\n */\n copyFailedMessage?: string;\n /**\n * The height of the content in its collapsed state.\n */\n collapsedHeight?: number;\n};\n\nconst CopyableContent: FC<CopyableContentProps> = ({\n appearance = CopyableContentAppearance.Default,\n content,\n children,\n copyFailedMessage,\n collapsedHeight,\n}) => {\n const rootRef = useRef<HTMLElement>(null);\n const actionGroupRef = useRef<HTMLDivElement>(null);\n const copyFeedbackTimeoutRef = useRef<number>();\n const isActionGroupSticky = useStickyActionState(rootRef, actionGroupRef);\n const [hasCopied, setHasCopied] = useState(false);\n\n const colorScheme = useColorScheme();\n const colorMode =\n colorScheme?.theme.colorMode === 'dark'\n ? CopyableContentColorMode.Dark\n : CopyableContentColorMode.Light;\n\n const defaultCopyButtonText = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.copy),\n });\n const defaultCopyFailedMessage = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.copyFailed),\n });\n const shareText = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.share),\n });\n\n const html = useMemo(() => formatStringToHtml(content).html, [content]);\n\n useEffect(\n () => () => {\n window.clearTimeout(copyFeedbackTimeoutRef.current);\n },\n [],\n );\n\n const showCopyFeedback = useCallback(() => {\n window.clearTimeout(copyFeedbackTimeoutRef.current);\n setHasCopied(true);\n\n copyFeedbackTimeoutRef.current = window.setTimeout(() => {\n setHasCopied(false);\n }, COPY_FEEDBACK_DURATION);\n }, []);\n\n const handleCopy = useCallback(async () => {\n try {\n await copyableContentToClipboard(content);\n showCopyFeedback();\n } catch {\n void createDialog({\n showCloseIcon: true,\n text: copyFailedMessage ?? defaultCopyFailedMessage,\n toastType: ToastType.ERROR,\n type: DialogType.TOAST,\n }).open();\n }\n }, [content, defaultCopyFailedMessage, copyFailedMessage, showCopyFeedback]);\n\n return (\n <StyledCopyableContent\n $appearance={appearance}\n $colorMode={colorMode}\n className=\"copyable-content\"\n ref={rootRef}\n >\n <StyledCopyableContentActions ref={actionGroupRef}>\n <StyledCopyableContentActionGroup>\n <StyledCopyableContentButton\n $colorMode={colorMode}\n $isSticky={isActionGroupSticky}\n aria-label={defaultCopyButtonText}\n onClick={() => {\n void handleCopy();\n }}\n type=\"button\"\n >\n <Icon icons={hasCopied ? ['fa fa-check'] : ['fa-light fa-copy']} />\n </StyledCopyableContentButton>\n <SharingContextMenu\n link={content}\n shouldShowCallingCodeAction={false}\n shouldShowCopyAction={false}\n shouldUseDefaultTriggerStyles={false}\n >\n <StyledCopyableContentButton\n $colorMode={colorMode}\n $isSticky={isActionGroupSticky}\n aria-label={shareText}\n type=\"button\"\n >\n <Icon icons={['fa fa-share-nodes']} />\n </StyledCopyableContentButton>\n </SharingContextMenu>\n </StyledCopyableContentActionGroup>\n </StyledCopyableContentActions>\n {typeof collapsedHeight === 'number' ? (\n <StyledCopyableContentTruncation>\n <Truncation collapsedHeight={collapsedHeight}>\n <StyledCopyableContentBody\n $colorMode={colorMode}\n $shouldShowPadding={false}\n >\n {children ?? <div dangerouslySetInnerHTML={{ __html: html }} />}\n </StyledCopyableContentBody>\n </Truncation>\n </StyledCopyableContentTruncation>\n ) : (\n <StyledCopyableContentBody $colorMode={colorMode}>\n {children ?? <div dangerouslySetInnerHTML={{ __html: html }} />}\n </StyledCopyableContentBody>\n )}\n </StyledCopyableContent>\n );\n};\n\nCopyableContent.displayName = 'CopyableContent';\n\nexport default CopyableContent;\n\nexport { CopyableContentAppearance } from './CopyableContent.types';\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"CopyableContent.js","names":["_format","require","_textstring","_chaynsApi","_react","_interopRequireWildcard","_textStrings","_interopRequireDefault","_useStickyActionState","_ColorSchemeProvider","_SharingContextMenu","_Icon","_Truncation","_CopyableContent","_CopyableContent2","_copyableContentClipboard","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","COPY_FEEDBACK_DURATION","CopyableContent","appearance","CopyableContentAppearance","Default","content","children","copyFailedMessage","collapsedHeight","transformClipboardHtml","rootRef","useRef","actionGroupRef","copyFeedbackTimeoutRef","isActionGroupSticky","useStickyActionState","hasCopied","setHasCopied","useState","colorScheme","useColorScheme","colorMode","theme","CopyableContentColorMode","Dark","Light","defaultCopyButtonText","useTextstringValue","textstring","ttsToITextString","textStrings","components","copyableContent","copy","defaultCopyFailedMessage","copyFailed","shareText","share","html","useMemo","formatStringToHtml","useEffect","window","clearTimeout","current","showCopyFeedback","useCallback","setTimeout","handleCopy","copyableContentToClipboard","createDialog","showCloseIcon","text","toastType","ToastType","ERROR","type","DialogType","TOAST","open","createElement","StyledCopyableContent","$appearance","$colorMode","className","ref","StyledCopyableContentActions","StyledCopyableContentActionGroup","StyledCopyableContentButton","$isSticky","onClick","icons","link","shouldShowCallingCodeAction","shouldShowCopyAction","shouldUseDefaultTriggerStyles","StyledCopyableContentTruncation","StyledCopyableContentBody","$shouldShowPadding","dangerouslySetInnerHTML","__html","displayName","_default","exports"],"sources":["../../../../src/components/copyable-content/CopyableContent.tsx"],"sourcesContent":["import { formatStringToHtml } from '@chayns-components/format';\nimport { ttsToITextString, useTextstringValue } from '@chayns-components/textstring';\nimport { createDialog, DialogType, ToastType } from 'chayns-api';\nimport React, { FC, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport textStrings from '../../constants/textStrings';\nimport { useStickyActionState } from '../../hooks/useStickyActionState';\nimport { useColorScheme } from '../color-scheme-provider/ColorSchemeProvider';\nimport SharingContextMenu from '../sharing-context-menu/SharingContextMenu';\nimport Icon from '../icon/Icon';\nimport Truncation from '../truncation/Truncation';\nimport {\n CopyableContentColorMode,\n StyledCopyableContent,\n StyledCopyableContentActionGroup,\n StyledCopyableContentActions,\n StyledCopyableContentBody,\n StyledCopyableContentButton,\n StyledCopyableContentTruncation,\n} from './CopyableContent.styles';\nimport { CopyableContentAppearance } from './CopyableContent.types';\nimport { copyableContentToClipboard } from './copyableContentClipboard';\n\nconst COPY_FEEDBACK_DURATION = 1500;\n\nexport type CopyableContentProps = {\n /**\n * Controls the visual surface of the content block.\n */\n appearance?: CopyableContentAppearance;\n /**\n * Markdown source used for rendering and clipboard data.\n */\n content: string;\n /**\n * Replaces only the visible rendered content and never the copied source.\n */\n children?: ReactNode;\n /**\n * Replaces the localized error message shown when copying fails.\n */\n copyFailedMessage?: string;\n /**\n * The height of the content in its collapsed state.\n */\n collapsedHeight?: number;\n /**\n * Transforms the generated HTML before it is written to the clipboard.\n */\n transformClipboardHtml?: (html: string) => string;\n};\n\nconst CopyableContent: FC<CopyableContentProps> = ({\n appearance = CopyableContentAppearance.Default,\n content,\n children,\n copyFailedMessage,\n collapsedHeight,\n transformClipboardHtml,\n}) => {\n const rootRef = useRef<HTMLElement>(null);\n const actionGroupRef = useRef<HTMLDivElement>(null);\n const copyFeedbackTimeoutRef = useRef<number>();\n const isActionGroupSticky = useStickyActionState(rootRef, actionGroupRef);\n const [hasCopied, setHasCopied] = useState(false);\n\n const colorScheme = useColorScheme();\n const colorMode =\n colorScheme?.theme.colorMode === 'dark'\n ? CopyableContentColorMode.Dark\n : CopyableContentColorMode.Light;\n\n const defaultCopyButtonText = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.copy),\n });\n const defaultCopyFailedMessage = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.copyFailed),\n });\n const shareText = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.share),\n });\n\n const html = useMemo(() => formatStringToHtml(content).html, [content]);\n\n useEffect(\n () => () => {\n window.clearTimeout(copyFeedbackTimeoutRef.current);\n },\n [],\n );\n\n const showCopyFeedback = useCallback(() => {\n window.clearTimeout(copyFeedbackTimeoutRef.current);\n setHasCopied(true);\n\n copyFeedbackTimeoutRef.current = window.setTimeout(() => {\n setHasCopied(false);\n }, COPY_FEEDBACK_DURATION);\n }, []);\n\n const handleCopy = useCallback(async () => {\n try {\n await copyableContentToClipboard(content, transformClipboardHtml);\n showCopyFeedback();\n } catch {\n void createDialog({\n showCloseIcon: true,\n text: copyFailedMessage ?? defaultCopyFailedMessage,\n toastType: ToastType.ERROR,\n type: DialogType.TOAST,\n }).open();\n }\n }, [\n content,\n defaultCopyFailedMessage,\n copyFailedMessage,\n showCopyFeedback,\n transformClipboardHtml,\n ]);\n\n return (\n <StyledCopyableContent\n $appearance={appearance}\n $colorMode={colorMode}\n className=\"copyable-content\"\n ref={rootRef}\n >\n <StyledCopyableContentActions ref={actionGroupRef}>\n <StyledCopyableContentActionGroup>\n <StyledCopyableContentButton\n $colorMode={colorMode}\n $isSticky={isActionGroupSticky}\n aria-label={defaultCopyButtonText}\n onClick={() => {\n void handleCopy();\n }}\n type=\"button\"\n >\n <Icon icons={hasCopied ? ['fa fa-check'] : ['fa-light fa-copy']} />\n </StyledCopyableContentButton>\n <SharingContextMenu\n link={content}\n shouldShowCallingCodeAction={false}\n shouldShowCopyAction={false}\n shouldUseDefaultTriggerStyles={false}\n >\n <StyledCopyableContentButton\n $colorMode={colorMode}\n $isSticky={isActionGroupSticky}\n aria-label={shareText}\n type=\"button\"\n >\n <Icon icons={['fa fa-share-nodes']} />\n </StyledCopyableContentButton>\n </SharingContextMenu>\n </StyledCopyableContentActionGroup>\n </StyledCopyableContentActions>\n {typeof collapsedHeight === 'number' ? (\n <StyledCopyableContentTruncation>\n <Truncation collapsedHeight={collapsedHeight}>\n <StyledCopyableContentBody\n $colorMode={colorMode}\n $shouldShowPadding={false}\n >\n {children ?? <div dangerouslySetInnerHTML={{ __html: html }} />}\n </StyledCopyableContentBody>\n </Truncation>\n </StyledCopyableContentTruncation>\n ) : (\n <StyledCopyableContentBody $colorMode={colorMode}>\n {children ?? <div dangerouslySetInnerHTML={{ __html: html }} />}\n </StyledCopyableContentBody>\n )}\n </StyledCopyableContent>\n );\n};\n\nCopyableContent.displayName = 'CopyableContent';\n\nexport default CopyableContent;\n\nexport { CopyableContentAppearance } from './CopyableContent.types';\n"],"mappings":"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","ignoreList":[]}
@@ -93,6 +93,20 @@ const readBlob = blob => new Promise((resolve, reject) => {
93
93
  const item = write.mock.calls[0][0][0];
94
94
  await (0, _vitest.expect)(readBlob(await item.getType('text/plain'))).resolves.toBe('source markdown');
95
95
  });
96
+ (0, _vitest.it)('passes the clipboard HTML transformer to the copy action', async () => {
97
+ const write = _vitest.vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();
98
+ const transformClipboardHtml = _vitest.vi.fn(html => `${html}<p>Styled</p>`);
99
+ (0, _react.render)(/*#__PURE__*/_react2.default.createElement(_CopyableContent.default, {
100
+ content: "source markdown",
101
+ transformClipboardHtml: transformClipboardHtml
102
+ }));
103
+ _react.fireEvent.click(_react.screen.getByRole('button', {
104
+ name: 'Kopieren'
105
+ }));
106
+ await (0, _react.waitFor)(() => (0, _vitest.expect)(transformClipboardHtml).toHaveBeenCalledOnce());
107
+ const item = write.mock.calls[0][0][0];
108
+ await (0, _vitest.expect)(readBlob(await item.getType('text/html'))).resolves.toContain('<p>Styled</p>');
109
+ });
96
110
  (0, _vitest.it)('shows a checkmark after a successful copy', async () => {
97
111
  _vitest.vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();
98
112
  const {
@@ -1 +1 @@
1
- {"version":3,"file":"CopyableContent.test.js","names":["_react","require","_react2","_interopRequireDefault","_vitest","_CopyableContent","e","__esModule","default","vi","mock","Textstring","textstring","fallback","TextstringProvider","children","ttsToITextString","useTextstringValue","resizeObserverCallbacks","ResizeObserverMock","constructor","callback","push","disconnect","observe","unobserve","stubGlobal","triggerResize","height","forEach","contentRect","icons","createElement","join","shouldShowCallingCodeAction","shouldShowCopyAction","importOriginal","createDialog","fn","open","readBlob","blob","Promise","resolve","reject","reader","FileReader","onerror","error","onload","result","readAsText","describe","it","render","content","expect","screen","getByRole","name","toBeInTheDocument","toHaveAttribute","document","querySelector","not","write","spyOn","navigator","clipboard","mockResolvedValue","fireEvent","click","waitFor","toHaveBeenCalled","item","calls","getType","resolves","toBe","container","collapsedHeight","Array","fill"],"sources":["../../../../src/components/copyable-content/CopyableContent.test.tsx"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-call */\nimport { fireEvent, render, screen, waitFor } from '@testing-library/react';\nimport React from 'react';\nimport { describe, expect, it, vi } from 'vitest';\nimport CopyableContent from './CopyableContent';\n\nvi.mock('@chayns-components/textstring', () => ({\n Textstring: ({ textstring }: { textstring: { fallback: string } }) => textstring.fallback,\n TextstringProvider: ({ children }: { children: React.ReactNode }) => children,\n ttsToITextString: (textstring: { fallback: string }) => textstring,\n useTextstringValue: ({ textstring }: { textstring: { fallback: string } }) =>\n textstring.fallback,\n}));\n\ntype ResizeObserverCallback = (entries: ResizeObserverEntry[]) => void;\n\nconst resizeObserverCallbacks: ResizeObserverCallback[] = [];\n\nclass ResizeObserverMock {\n constructor(callback: ResizeObserverCallback) {\n resizeObserverCallbacks.push(callback);\n }\n\n disconnect() {}\n\n observe() {}\n\n unobserve() {}\n}\n\nvi.stubGlobal('ResizeObserver', ResizeObserverMock);\n\nconst triggerResize = (height: number) => {\n resizeObserverCallbacks.forEach((callback) => {\n callback([\n {\n contentRect: { height } as DOMRectReadOnly,\n } as ResizeObserverEntry,\n ]);\n });\n};\n\nvi.mock('../icon/Icon', () => ({\n default: ({ icons }: { icons: string[] }) => <span data-icons={icons.join(' ')} />,\n}));\n\nvi.mock('../sharing-context-menu/SharingContextMenu', () => ({\n default: ({\n children,\n shouldShowCallingCodeAction,\n shouldShowCopyAction,\n }: {\n children: React.ReactNode;\n shouldShowCallingCodeAction?: boolean;\n shouldShowCopyAction?: boolean;\n }) => (\n <div\n data-calling-code-action={shouldShowCallingCodeAction}\n data-copy-action={shouldShowCopyAction}\n >\n {children}\n </div>\n ),\n}));\n\nvi.mock('chayns-api', async (importOriginal) => ({\n ...(await importOriginal<typeof import('chayns-api')>()),\n createDialog: vi.fn(() => ({ open: vi.fn() })),\n}));\n\nconst readBlob = (blob: Blob) =>\n new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = () => reject(reader.error);\n reader.onload = () => resolve(reader.result as string);\n reader.readAsText(blob);\n });\n\ndescribe('CopyableContent', () => {\n it('renders formatted markdown without active input HTML', () => {\n render(\n <CopyableContent\n content={\n '# Heading\\n\\n- Entry\\n\\n[Link](https://example.com)\\n<script onload=\"x\">bad</script>'\n }\n />,\n );\n\n expect(screen.getByRole('heading', { name: 'Heading' })).toBeInTheDocument();\n expect(screen.getByRole('link', { name: 'Link' })).toHaveAttribute(\n 'href',\n 'https://example.com',\n );\n expect(document.querySelector('script')).not.toBeInTheDocument();\n expect(document.querySelector('[onload]')).not.toBeInTheDocument();\n });\n\n it('copies content instead of custom children', async () => {\n const write = vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n render(<CopyableContent content=\"source markdown\">Visible replacement</CopyableContent>);\n\n fireEvent.click(screen.getByRole('button', { name: 'Kopieren' }));\n\n await waitFor(() => expect(write).toHaveBeenCalled());\n const item = write.mock.calls[0][0][0] as unknown as ClipboardItem;\n await expect(readBlob(await item.getType('text/plain'))).resolves.toBe('source markdown');\n });\n\n it('shows a checkmark after a successful copy', async () => {\n vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n const { container } = render(<CopyableContent content=\"source\" />);\n\n fireEvent.click(screen.getByRole('button', { name: 'Kopieren' }));\n\n await waitFor(() => {\n expect(container.querySelector('[data-icons=\"fa fa-check\"]')).toBeInTheDocument();\n });\n });\n\n it('offers sharing actions', () => {\n const { container } = render(<CopyableContent content=\"source\" />);\n\n expect(screen.getByRole('button', { name: 'Teilen' })).toBeInTheDocument();\n expect(container.querySelector('[data-copy-action=\"false\"]')).toBeInTheDocument();\n expect(container.querySelector('[data-calling-code-action=\"false\"]')).toBeInTheDocument();\n });\n\n it('supports collapsed content', async () => {\n render(\n <CopyableContent\n collapsedHeight={100}\n content={Array(20).fill('Long content').join('\\n\\n')}\n />,\n );\n\n triggerResize(240);\n\n await waitFor(() => {\n expect(screen.getByRole('button', { name: 'Mehr' })).toBeInTheDocument();\n });\n });\n});\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"CopyableContent.test.js","names":["_react","require","_react2","_interopRequireDefault","_vitest","_CopyableContent","e","__esModule","default","vi","mock","Textstring","textstring","fallback","TextstringProvider","children","ttsToITextString","useTextstringValue","resizeObserverCallbacks","ResizeObserverMock","constructor","callback","push","disconnect","observe","unobserve","stubGlobal","triggerResize","height","forEach","contentRect","icons","createElement","join","shouldShowCallingCodeAction","shouldShowCopyAction","importOriginal","createDialog","fn","open","readBlob","blob","Promise","resolve","reject","reader","FileReader","onerror","error","onload","result","readAsText","describe","it","render","content","expect","screen","getByRole","name","toBeInTheDocument","toHaveAttribute","document","querySelector","not","write","spyOn","navigator","clipboard","mockResolvedValue","fireEvent","click","waitFor","toHaveBeenCalled","item","calls","getType","resolves","toBe","transformClipboardHtml","html","toHaveBeenCalledOnce","toContain","container","collapsedHeight","Array","fill"],"sources":["../../../../src/components/copyable-content/CopyableContent.test.tsx"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-call */\nimport { fireEvent, render, screen, waitFor } from '@testing-library/react';\nimport React from 'react';\nimport { describe, expect, it, vi } from 'vitest';\nimport CopyableContent from './CopyableContent';\n\nvi.mock('@chayns-components/textstring', () => ({\n Textstring: ({ textstring }: { textstring: { fallback: string } }) => textstring.fallback,\n TextstringProvider: ({ children }: { children: React.ReactNode }) => children,\n ttsToITextString: (textstring: { fallback: string }) => textstring,\n useTextstringValue: ({ textstring }: { textstring: { fallback: string } }) =>\n textstring.fallback,\n}));\n\ntype ResizeObserverCallback = (entries: ResizeObserverEntry[]) => void;\n\nconst resizeObserverCallbacks: ResizeObserverCallback[] = [];\n\nclass ResizeObserverMock {\n constructor(callback: ResizeObserverCallback) {\n resizeObserverCallbacks.push(callback);\n }\n\n disconnect() {}\n\n observe() {}\n\n unobserve() {}\n}\n\nvi.stubGlobal('ResizeObserver', ResizeObserverMock);\n\nconst triggerResize = (height: number) => {\n resizeObserverCallbacks.forEach((callback) => {\n callback([\n {\n contentRect: { height } as DOMRectReadOnly,\n } as ResizeObserverEntry,\n ]);\n });\n};\n\nvi.mock('../icon/Icon', () => ({\n default: ({ icons }: { icons: string[] }) => <span data-icons={icons.join(' ')} />,\n}));\n\nvi.mock('../sharing-context-menu/SharingContextMenu', () => ({\n default: ({\n children,\n shouldShowCallingCodeAction,\n shouldShowCopyAction,\n }: {\n children: React.ReactNode;\n shouldShowCallingCodeAction?: boolean;\n shouldShowCopyAction?: boolean;\n }) => (\n <div\n data-calling-code-action={shouldShowCallingCodeAction}\n data-copy-action={shouldShowCopyAction}\n >\n {children}\n </div>\n ),\n}));\n\nvi.mock('chayns-api', async (importOriginal) => ({\n ...(await importOriginal<typeof import('chayns-api')>()),\n createDialog: vi.fn(() => ({ open: vi.fn() })),\n}));\n\nconst readBlob = (blob: Blob) =>\n new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = () => reject(reader.error);\n reader.onload = () => resolve(reader.result as string);\n reader.readAsText(blob);\n });\n\ndescribe('CopyableContent', () => {\n it('renders formatted markdown without active input HTML', () => {\n render(\n <CopyableContent\n content={\n '# Heading\\n\\n- Entry\\n\\n[Link](https://example.com)\\n<script onload=\"x\">bad</script>'\n }\n />,\n );\n\n expect(screen.getByRole('heading', { name: 'Heading' })).toBeInTheDocument();\n expect(screen.getByRole('link', { name: 'Link' })).toHaveAttribute(\n 'href',\n 'https://example.com',\n );\n expect(document.querySelector('script')).not.toBeInTheDocument();\n expect(document.querySelector('[onload]')).not.toBeInTheDocument();\n });\n\n it('copies content instead of custom children', async () => {\n const write = vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n render(<CopyableContent content=\"source markdown\">Visible replacement</CopyableContent>);\n\n fireEvent.click(screen.getByRole('button', { name: 'Kopieren' }));\n\n await waitFor(() => expect(write).toHaveBeenCalled());\n const item = write.mock.calls[0][0][0] as unknown as ClipboardItem;\n await expect(readBlob(await item.getType('text/plain'))).resolves.toBe('source markdown');\n });\n\n it('passes the clipboard HTML transformer to the copy action', async () => {\n const write = vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n const transformClipboardHtml = vi.fn((html: string) => `${html}<p>Styled</p>`);\n\n render(\n <CopyableContent\n content=\"source markdown\"\n transformClipboardHtml={transformClipboardHtml}\n />,\n );\n\n fireEvent.click(screen.getByRole('button', { name: 'Kopieren' }));\n\n await waitFor(() => expect(transformClipboardHtml).toHaveBeenCalledOnce());\n const item = write.mock.calls[0][0][0];\n\n await expect(readBlob(await item.getType('text/html'))).resolves.toContain('<p>Styled</p>');\n });\n\n it('shows a checkmark after a successful copy', async () => {\n vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n const { container } = render(<CopyableContent content=\"source\" />);\n\n fireEvent.click(screen.getByRole('button', { name: 'Kopieren' }));\n\n await waitFor(() => {\n expect(container.querySelector('[data-icons=\"fa fa-check\"]')).toBeInTheDocument();\n });\n });\n\n it('offers sharing actions', () => {\n const { container } = render(<CopyableContent content=\"source\" />);\n\n expect(screen.getByRole('button', { name: 'Teilen' })).toBeInTheDocument();\n expect(container.querySelector('[data-copy-action=\"false\"]')).toBeInTheDocument();\n expect(container.querySelector('[data-calling-code-action=\"false\"]')).toBeInTheDocument();\n });\n\n it('supports collapsed content', async () => {\n render(\n <CopyableContent\n collapsedHeight={100}\n content={Array(20).fill('Long content').join('\\n\\n')}\n />,\n );\n\n triggerResize(240);\n\n await waitFor(() => {\n expect(screen.getByRole('button', { name: 'Mehr' })).toBeInTheDocument();\n });\n });\n});\n"],"mappings":"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","ignoreList":[]}
@@ -18,13 +18,14 @@ const getPlainTextFromHtml = html => {
18
18
  return ((_element$textContent = element.textContent) === null || _element$textContent === void 0 ? void 0 : _element$textContent.trim()) ?? '';
19
19
  }).filter(Boolean).join('\n\n');
20
20
  };
21
- const createClipboardContent = source => {
21
+ const createClipboardContent = (source, transformHtml) => {
22
22
  const {
23
23
  html
24
24
  } = (0, _format.formatStringToHtml)(source);
25
+ const clipboardHtml = (transformHtml === null || transformHtml === void 0 ? void 0 : transformHtml(html)) ?? html;
25
26
  return {
26
- html,
27
- plainText: getPlainTextFromHtml(html)
27
+ html: clipboardHtml,
28
+ plainText: getPlainTextFromHtml(clipboardHtml)
28
29
  };
29
30
  };
30
31
  const copyWithClipboardItem = async ({
@@ -56,8 +57,8 @@ const copyPlainText = async plainText => {
56
57
  }
57
58
  await navigator.clipboard.writeText(plainText);
58
59
  };
59
- const copyableContentToClipboard = async source => {
60
- const clipboardContent = createClipboardContent(source);
60
+ const copyableContentToClipboard = async (source, transformHtml) => {
61
+ const clipboardContent = createClipboardContent(source, transformHtml);
61
62
  if (!navigator.clipboard) {
62
63
  throw new Error('Clipboard API is not available.');
63
64
  }
@@ -1 +1 @@
1
- {"version":3,"file":"copyableContentClipboard.js","names":["_format","require","getPlainTextFromHtml","html","document","DOMParser","parseFromString","Array","from","body","children","map","element","_element$textContent","tagName","item","_item$textContent","textContent","trim","join","filter","Boolean","createClipboardContent","source","formatStringToHtml","plainText","copyWithClipboardItem","clipboardData","ClipboardItem","Blob","type","navigator","clipboard","write","copyPlainText","writeText","copyableContentToClipboard","clipboardContent","Error","exports"],"sources":["../../../../src/components/copyable-content/copyableContentClipboard.ts"],"sourcesContent":["import { formatStringToHtml } from '@chayns-components/format';\n\ninterface ClipboardContent {\n html: string;\n plainText: string;\n}\n\nconst getPlainTextFromHtml = (html: string) => {\n const document = new DOMParser().parseFromString(html, 'text/html');\n\n return Array.from(document.body.children)\n .map((element) => {\n if (element.tagName === 'UL' || element.tagName === 'OL') {\n return Array.from(element.children)\n .map((item) => `• ${item.textContent?.trim() ?? ''}`)\n .join('\\n');\n }\n\n return element.textContent?.trim() ?? '';\n })\n .filter(Boolean)\n .join('\\n\\n');\n};\n\nconst createClipboardContent = (source: string): ClipboardContent => {\n const { html } = formatStringToHtml(source);\n return { html, plainText: getPlainTextFromHtml(html) };\n};\n\nconst copyWithClipboardItem = async ({ html, plainText }: ClipboardContent) => {\n const clipboardData = new ClipboardItem({\n 'text/plain': new Blob([plainText], { type: 'text/plain' }),\n 'text/html': new Blob([html], { type: 'text/html' }),\n });\n\n await navigator.clipboard.write([clipboardData]);\n};\n\nconst copyPlainText = async (plainText: string) => {\n if (typeof ClipboardItem !== 'undefined') {\n try {\n await navigator.clipboard.write([\n new ClipboardItem({\n 'text/plain': new Blob([plainText], { type: 'text/plain' }),\n }),\n ]);\n return;\n } catch {\n // Use writeText as the final browser-compatible fallback.\n }\n }\n\n await navigator.clipboard.writeText(plainText);\n};\n\nexport const copyableContentToClipboard = async (source: string) => {\n const clipboardContent = createClipboardContent(source);\n\n if (!navigator.clipboard) {\n throw new Error('Clipboard API is not available.');\n }\n\n if (typeof ClipboardItem === 'undefined') {\n await copyPlainText(clipboardContent.plainText);\n return;\n }\n\n try {\n await copyWithClipboardItem(clipboardContent);\n } catch {\n // Use the plain-text fallback when the browser rejects rich MIME types.\n await copyPlainText(clipboardContent.plainText);\n }\n};\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"copyableContentClipboard.js","names":["_format","require","getPlainTextFromHtml","html","document","DOMParser","parseFromString","Array","from","body","children","map","element","_element$textContent","tagName","item","_item$textContent","textContent","trim","join","filter","Boolean","createClipboardContent","source","transformHtml","formatStringToHtml","clipboardHtml","plainText","copyWithClipboardItem","clipboardData","ClipboardItem","Blob","type","navigator","clipboard","write","copyPlainText","writeText","copyableContentToClipboard","clipboardContent","Error","exports"],"sources":["../../../../src/components/copyable-content/copyableContentClipboard.ts"],"sourcesContent":["import { formatStringToHtml } from '@chayns-components/format';\n\ninterface ClipboardContent {\n html: string;\n plainText: string;\n}\n\nconst getPlainTextFromHtml = (html: string) => {\n const document = new DOMParser().parseFromString(html, 'text/html');\n\n return Array.from(document.body.children)\n .map((element) => {\n if (element.tagName === 'UL' || element.tagName === 'OL') {\n return Array.from(element.children)\n .map((item) => `• ${item.textContent?.trim() ?? ''}`)\n .join('\\n');\n }\n\n return element.textContent?.trim() ?? '';\n })\n .filter(Boolean)\n .join('\\n\\n');\n};\n\nconst createClipboardContent = (\n source: string,\n transformHtml?: (html: string) => string,\n): ClipboardContent => {\n const { html } = formatStringToHtml(source);\n const clipboardHtml = transformHtml?.(html) ?? html;\n\n return { html: clipboardHtml, plainText: getPlainTextFromHtml(clipboardHtml) };\n};\n\nconst copyWithClipboardItem = async ({ html, plainText }: ClipboardContent) => {\n const clipboardData = new ClipboardItem({\n 'text/plain': new Blob([plainText], { type: 'text/plain' }),\n 'text/html': new Blob([html], { type: 'text/html' }),\n });\n\n await navigator.clipboard.write([clipboardData]);\n};\n\nconst copyPlainText = async (plainText: string) => {\n if (typeof ClipboardItem !== 'undefined') {\n try {\n await navigator.clipboard.write([\n new ClipboardItem({\n 'text/plain': new Blob([plainText], { type: 'text/plain' }),\n }),\n ]);\n return;\n } catch {\n // Use writeText as the final browser-compatible fallback.\n }\n }\n\n await navigator.clipboard.writeText(plainText);\n};\n\nexport const copyableContentToClipboard = async (\n source: string,\n transformHtml?: (html: string) => string,\n) => {\n const clipboardContent = createClipboardContent(source, transformHtml);\n\n if (!navigator.clipboard) {\n throw new Error('Clipboard API is not available.');\n }\n\n if (typeof ClipboardItem === 'undefined') {\n await copyPlainText(clipboardContent.plainText);\n return;\n }\n\n try {\n await copyWithClipboardItem(clipboardContent);\n } catch {\n // Use the plain-text fallback when the browser rejects rich MIME types.\n await copyPlainText(clipboardContent.plainText);\n }\n};\n"],"mappings":"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","ignoreList":[]}
@@ -11,6 +11,12 @@ const readBlob = blob => new Promise((resolve, reject) => {
11
11
  reader.readAsText(blob);
12
12
  });
13
13
  (0, _vitest.describe)('copyableContentToClipboard', () => {
14
+ (0, _vitest.it)('transforms the generated HTML before copying', async () => {
15
+ const write = _vitest.vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();
16
+ await (0, _copyableContentClipboard.copyableContentToClipboard)('source', html => `${html}<p>Styled</p>`);
17
+ const clipboardItem = write.mock.calls[0][0][0];
18
+ await (0, _vitest.expect)(readBlob(await clipboardItem.getType('text/html'))).resolves.toContain('<p>Styled</p>');
19
+ });
14
20
  (0, _vitest.afterEach)(() => {
15
21
  _vitest.vi.restoreAllMocks();
16
22
  _vitest.vi.unstubAllGlobals();
@@ -1 +1 @@
1
- {"version":3,"file":"copyableContentClipboard.test.js","names":["_format","require","_vitest","_copyableContentClipboard","source","readBlob","blob","Promise","resolve","reject","reader","FileReader","onerror","error","onload","result","readAsText","describe","afterEach","vi","restoreAllMocks","unstubAllGlobals","it","write","spyOn","navigator","clipboard","mockResolvedValue","copyableContentToClipboard","item","mock","calls","expect","Object","keys","items","toEqual","getType","resolves","toBe","formatStringToHtml","html","mockRejectedValueOnce","Error","mockResolvedValueOnce","writeText","toHaveBeenCalledTimes","not","toHaveBeenCalled","mockRejectedValue","toHaveBeenCalledWith","stubGlobal","undefined"],"sources":["../../../../src/components/copyable-content/copyableContentClipboard.test.ts"],"sourcesContent":["import { formatStringToHtml } from '@chayns-components/format';\nimport { afterEach, describe, expect, it, vi } from 'vitest';\nimport { copyableContentToClipboard } from './copyableContentClipboard';\n\nconst source = '# Heading\\n\\n[Link](https://example.com)';\n\nconst readBlob = (blob: Blob) =>\n new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = () => reject(reader.error);\n reader.onload = () => resolve(reader.result as string);\n reader.readAsText(blob);\n });\n\ndescribe('copyableContentToClipboard', () => {\n afterEach(() => {\n vi.restoreAllMocks();\n vi.unstubAllGlobals();\n });\n\n it('writes readable plain text first and safe HTML second', async () => {\n const write = vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n const item = write.mock.calls[0][0][0] as unknown as ClipboardItem & {\n items: Record<string, Blob>;\n };\n expect(Object.keys(item.items)).toEqual(['text/plain', 'text/html']);\n await expect(readBlob(await item.getType('text/plain'))).resolves.toBe('Heading\\n\\nLink');\n await expect(readBlob(await item.getType('text/html'))).resolves.toBe(\n formatStringToHtml(source).html,\n );\n });\n\n it('uses a plain ClipboardItem after a rejected rich write', async () => {\n const write = vi\n .spyOn(navigator.clipboard, 'write')\n .mockRejectedValueOnce(new Error('denied'))\n .mockResolvedValueOnce();\n const writeText = vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n expect(write).toHaveBeenCalledTimes(2);\n expect(writeText).not.toHaveBeenCalled();\n const item = write.mock.calls[1][0][0] as unknown as ClipboardItem & {\n items: Record<string, Blob>;\n };\n expect(Object.keys(item.items)).toEqual(['text/plain']);\n await expect(readBlob(await item.getType('text/plain'))).resolves.toBe('Heading\\n\\nLink');\n });\n\n it('uses writeText after all ClipboardItem writes fail', async () => {\n vi.spyOn(navigator.clipboard, 'write').mockRejectedValue(new Error('denied'));\n const writeText = vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n expect(writeText).toHaveBeenCalledWith('Heading\\n\\nLink');\n });\n\n it('uses writeText when ClipboardItem is unavailable', async () => {\n vi.stubGlobal('ClipboardItem', undefined);\n const writeText = vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n expect(writeText).toHaveBeenCalledWith('Heading\\n\\nLink');\n });\n});\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"copyableContentClipboard.test.js","names":["_format","require","_vitest","_copyableContentClipboard","source","readBlob","blob","Promise","resolve","reject","reader","FileReader","onerror","error","onload","result","readAsText","describe","it","write","vi","spyOn","navigator","clipboard","mockResolvedValue","copyableContentToClipboard","html","clipboardItem","mock","calls","expect","getType","resolves","toContain","afterEach","restoreAllMocks","unstubAllGlobals","item","Object","keys","items","toEqual","toBe","formatStringToHtml","mockRejectedValueOnce","Error","mockResolvedValueOnce","writeText","toHaveBeenCalledTimes","not","toHaveBeenCalled","mockRejectedValue","toHaveBeenCalledWith","stubGlobal","undefined"],"sources":["../../../../src/components/copyable-content/copyableContentClipboard.test.ts"],"sourcesContent":["import { formatStringToHtml } from '@chayns-components/format';\nimport { afterEach, describe, expect, it, vi } from 'vitest';\nimport { copyableContentToClipboard } from './copyableContentClipboard';\n\nconst source = '# Heading\\n\\n[Link](https://example.com)';\n\nconst readBlob = (blob: Blob) =>\n new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = () => reject(reader.error);\n reader.onload = () => resolve(reader.result as string);\n reader.readAsText(blob);\n });\n\ndescribe('copyableContentToClipboard', () => {\n it('transforms the generated HTML before copying', async () => {\n const write = vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n\n await copyableContentToClipboard('source', (html) => `${html}<p>Styled</p>`);\n\n const clipboardItem = write.mock.calls[0][0][0];\n\n await expect(readBlob(await clipboardItem.getType('text/html'))).resolves.toContain(\n '<p>Styled</p>',\n );\n });\n\n afterEach(() => {\n vi.restoreAllMocks();\n vi.unstubAllGlobals();\n });\n\n it('writes readable plain text first and safe HTML second', async () => {\n const write = vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n const item = write.mock.calls[0][0][0] as unknown as ClipboardItem & {\n items: Record<string, Blob>;\n };\n expect(Object.keys(item.items)).toEqual(['text/plain', 'text/html']);\n await expect(readBlob(await item.getType('text/plain'))).resolves.toBe('Heading\\n\\nLink');\n await expect(readBlob(await item.getType('text/html'))).resolves.toBe(\n formatStringToHtml(source).html,\n );\n });\n\n it('uses a plain ClipboardItem after a rejected rich write', async () => {\n const write = vi\n .spyOn(navigator.clipboard, 'write')\n .mockRejectedValueOnce(new Error('denied'))\n .mockResolvedValueOnce();\n const writeText = vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n expect(write).toHaveBeenCalledTimes(2);\n expect(writeText).not.toHaveBeenCalled();\n const item = write.mock.calls[1][0][0] as unknown as ClipboardItem & {\n items: Record<string, Blob>;\n };\n expect(Object.keys(item.items)).toEqual(['text/plain']);\n await expect(readBlob(await item.getType('text/plain'))).resolves.toBe('Heading\\n\\nLink');\n });\n\n it('uses writeText after all ClipboardItem writes fail', async () => {\n vi.spyOn(navigator.clipboard, 'write').mockRejectedValue(new Error('denied'));\n const writeText = vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n expect(writeText).toHaveBeenCalledWith('Heading\\n\\nLink');\n });\n\n it('uses writeText when ClipboardItem is unavailable', async () => {\n vi.stubGlobal('ClipboardItem', undefined);\n const writeText = vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n expect(writeText).toHaveBeenCalledWith('Heading\\n\\nLink');\n });\n});\n"],"mappings":"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","ignoreList":[]}
@@ -17,7 +17,8 @@ const CopyableContent = ({
17
17
  content,
18
18
  children,
19
19
  copyFailedMessage,
20
- collapsedHeight
20
+ collapsedHeight,
21
+ transformClipboardHtml
21
22
  }) => {
22
23
  const rootRef = useRef(null);
23
24
  const actionGroupRef = useRef(null);
@@ -48,7 +49,7 @@ const CopyableContent = ({
48
49
  }, []);
49
50
  const handleCopy = useCallback(async () => {
50
51
  try {
51
- await copyableContentToClipboard(content);
52
+ await copyableContentToClipboard(content, transformClipboardHtml);
52
53
  showCopyFeedback();
53
54
  } catch {
54
55
  void createDialog({
@@ -58,7 +59,7 @@ const CopyableContent = ({
58
59
  type: DialogType.TOAST
59
60
  }).open();
60
61
  }
61
- }, [content, defaultCopyFailedMessage, copyFailedMessage, showCopyFeedback]);
62
+ }, [content, defaultCopyFailedMessage, copyFailedMessage, showCopyFeedback, transformClipboardHtml]);
62
63
  return /*#__PURE__*/React.createElement(StyledCopyableContent, {
63
64
  $appearance: appearance,
64
65
  $colorMode: colorMode,
@@ -1 +1 @@
1
- {"version":3,"file":"CopyableContent.js","names":["formatStringToHtml","ttsToITextString","useTextstringValue","createDialog","DialogType","ToastType","React","useCallback","useEffect","useMemo","useRef","useState","textStrings","useStickyActionState","useColorScheme","SharingContextMenu","Icon","Truncation","CopyableContentColorMode","StyledCopyableContent","StyledCopyableContentActionGroup","StyledCopyableContentActions","StyledCopyableContentBody","StyledCopyableContentButton","StyledCopyableContentTruncation","CopyableContentAppearance","copyableContentToClipboard","COPY_FEEDBACK_DURATION","CopyableContent","appearance","Default","content","children","copyFailedMessage","collapsedHeight","rootRef","actionGroupRef","copyFeedbackTimeoutRef","isActionGroupSticky","hasCopied","setHasCopied","colorScheme","colorMode","theme","Dark","Light","defaultCopyButtonText","textstring","components","copyableContent","copy","defaultCopyFailedMessage","copyFailed","shareText","share","html","window","clearTimeout","current","showCopyFeedback","setTimeout","handleCopy","showCloseIcon","text","toastType","ERROR","type","TOAST","open","createElement","$appearance","$colorMode","className","ref","$isSticky","onClick","icons","link","shouldShowCallingCodeAction","shouldShowCopyAction","shouldUseDefaultTriggerStyles","$shouldShowPadding","dangerouslySetInnerHTML","__html","displayName"],"sources":["../../../../src/components/copyable-content/CopyableContent.tsx"],"sourcesContent":["import { formatStringToHtml } from '@chayns-components/format';\nimport { ttsToITextString, useTextstringValue } from '@chayns-components/textstring';\nimport { createDialog, DialogType, ToastType } from 'chayns-api';\nimport React, { FC, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport textStrings from '../../constants/textStrings';\nimport { useStickyActionState } from '../../hooks/useStickyActionState';\nimport { useColorScheme } from '../color-scheme-provider/ColorSchemeProvider';\nimport SharingContextMenu from '../sharing-context-menu/SharingContextMenu';\nimport Icon from '../icon/Icon';\nimport Truncation from '../truncation/Truncation';\nimport {\n CopyableContentColorMode,\n StyledCopyableContent,\n StyledCopyableContentActionGroup,\n StyledCopyableContentActions,\n StyledCopyableContentBody,\n StyledCopyableContentButton,\n StyledCopyableContentTruncation,\n} from './CopyableContent.styles';\nimport { CopyableContentAppearance } from './CopyableContent.types';\nimport { copyableContentToClipboard } from './copyableContentClipboard';\n\nconst COPY_FEEDBACK_DURATION = 1500;\n\nexport type CopyableContentProps = {\n /**\n * Controls the visual surface of the content block.\n */\n appearance?: CopyableContentAppearance;\n /**\n * Markdown source used for rendering and clipboard data.\n */\n content: string;\n /**\n * Replaces only the visible rendered content and never the copied source.\n */\n children?: ReactNode;\n /**\n * Replaces the localized error message shown when copying fails.\n */\n copyFailedMessage?: string;\n /**\n * The height of the content in its collapsed state.\n */\n collapsedHeight?: number;\n};\n\nconst CopyableContent: FC<CopyableContentProps> = ({\n appearance = CopyableContentAppearance.Default,\n content,\n children,\n copyFailedMessage,\n collapsedHeight,\n}) => {\n const rootRef = useRef<HTMLElement>(null);\n const actionGroupRef = useRef<HTMLDivElement>(null);\n const copyFeedbackTimeoutRef = useRef<number>();\n const isActionGroupSticky = useStickyActionState(rootRef, actionGroupRef);\n const [hasCopied, setHasCopied] = useState(false);\n\n const colorScheme = useColorScheme();\n const colorMode =\n colorScheme?.theme.colorMode === 'dark'\n ? CopyableContentColorMode.Dark\n : CopyableContentColorMode.Light;\n\n const defaultCopyButtonText = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.copy),\n });\n const defaultCopyFailedMessage = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.copyFailed),\n });\n const shareText = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.share),\n });\n\n const html = useMemo(() => formatStringToHtml(content).html, [content]);\n\n useEffect(\n () => () => {\n window.clearTimeout(copyFeedbackTimeoutRef.current);\n },\n [],\n );\n\n const showCopyFeedback = useCallback(() => {\n window.clearTimeout(copyFeedbackTimeoutRef.current);\n setHasCopied(true);\n\n copyFeedbackTimeoutRef.current = window.setTimeout(() => {\n setHasCopied(false);\n }, COPY_FEEDBACK_DURATION);\n }, []);\n\n const handleCopy = useCallback(async () => {\n try {\n await copyableContentToClipboard(content);\n showCopyFeedback();\n } catch {\n void createDialog({\n showCloseIcon: true,\n text: copyFailedMessage ?? defaultCopyFailedMessage,\n toastType: ToastType.ERROR,\n type: DialogType.TOAST,\n }).open();\n }\n }, [content, defaultCopyFailedMessage, copyFailedMessage, showCopyFeedback]);\n\n return (\n <StyledCopyableContent\n $appearance={appearance}\n $colorMode={colorMode}\n className=\"copyable-content\"\n ref={rootRef}\n >\n <StyledCopyableContentActions ref={actionGroupRef}>\n <StyledCopyableContentActionGroup>\n <StyledCopyableContentButton\n $colorMode={colorMode}\n $isSticky={isActionGroupSticky}\n aria-label={defaultCopyButtonText}\n onClick={() => {\n void handleCopy();\n }}\n type=\"button\"\n >\n <Icon icons={hasCopied ? ['fa fa-check'] : ['fa-light fa-copy']} />\n </StyledCopyableContentButton>\n <SharingContextMenu\n link={content}\n shouldShowCallingCodeAction={false}\n shouldShowCopyAction={false}\n shouldUseDefaultTriggerStyles={false}\n >\n <StyledCopyableContentButton\n $colorMode={colorMode}\n $isSticky={isActionGroupSticky}\n aria-label={shareText}\n type=\"button\"\n >\n <Icon icons={['fa fa-share-nodes']} />\n </StyledCopyableContentButton>\n </SharingContextMenu>\n </StyledCopyableContentActionGroup>\n </StyledCopyableContentActions>\n {typeof collapsedHeight === 'number' ? (\n <StyledCopyableContentTruncation>\n <Truncation collapsedHeight={collapsedHeight}>\n <StyledCopyableContentBody\n $colorMode={colorMode}\n $shouldShowPadding={false}\n >\n {children ?? <div dangerouslySetInnerHTML={{ __html: html }} />}\n </StyledCopyableContentBody>\n </Truncation>\n </StyledCopyableContentTruncation>\n ) : (\n <StyledCopyableContentBody $colorMode={colorMode}>\n {children ?? <div dangerouslySetInnerHTML={{ __html: html }} />}\n </StyledCopyableContentBody>\n )}\n </StyledCopyableContent>\n );\n};\n\nCopyableContent.displayName = 'CopyableContent';\n\nexport default CopyableContent;\n\nexport { CopyableContentAppearance } from './CopyableContent.types';\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"CopyableContent.js","names":["formatStringToHtml","ttsToITextString","useTextstringValue","createDialog","DialogType","ToastType","React","useCallback","useEffect","useMemo","useRef","useState","textStrings","useStickyActionState","useColorScheme","SharingContextMenu","Icon","Truncation","CopyableContentColorMode","StyledCopyableContent","StyledCopyableContentActionGroup","StyledCopyableContentActions","StyledCopyableContentBody","StyledCopyableContentButton","StyledCopyableContentTruncation","CopyableContentAppearance","copyableContentToClipboard","COPY_FEEDBACK_DURATION","CopyableContent","appearance","Default","content","children","copyFailedMessage","collapsedHeight","transformClipboardHtml","rootRef","actionGroupRef","copyFeedbackTimeoutRef","isActionGroupSticky","hasCopied","setHasCopied","colorScheme","colorMode","theme","Dark","Light","defaultCopyButtonText","textstring","components","copyableContent","copy","defaultCopyFailedMessage","copyFailed","shareText","share","html","window","clearTimeout","current","showCopyFeedback","setTimeout","handleCopy","showCloseIcon","text","toastType","ERROR","type","TOAST","open","createElement","$appearance","$colorMode","className","ref","$isSticky","onClick","icons","link","shouldShowCallingCodeAction","shouldShowCopyAction","shouldUseDefaultTriggerStyles","$shouldShowPadding","dangerouslySetInnerHTML","__html","displayName"],"sources":["../../../../src/components/copyable-content/CopyableContent.tsx"],"sourcesContent":["import { formatStringToHtml } from '@chayns-components/format';\nimport { ttsToITextString, useTextstringValue } from '@chayns-components/textstring';\nimport { createDialog, DialogType, ToastType } from 'chayns-api';\nimport React, { FC, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport textStrings from '../../constants/textStrings';\nimport { useStickyActionState } from '../../hooks/useStickyActionState';\nimport { useColorScheme } from '../color-scheme-provider/ColorSchemeProvider';\nimport SharingContextMenu from '../sharing-context-menu/SharingContextMenu';\nimport Icon from '../icon/Icon';\nimport Truncation from '../truncation/Truncation';\nimport {\n CopyableContentColorMode,\n StyledCopyableContent,\n StyledCopyableContentActionGroup,\n StyledCopyableContentActions,\n StyledCopyableContentBody,\n StyledCopyableContentButton,\n StyledCopyableContentTruncation,\n} from './CopyableContent.styles';\nimport { CopyableContentAppearance } from './CopyableContent.types';\nimport { copyableContentToClipboard } from './copyableContentClipboard';\n\nconst COPY_FEEDBACK_DURATION = 1500;\n\nexport type CopyableContentProps = {\n /**\n * Controls the visual surface of the content block.\n */\n appearance?: CopyableContentAppearance;\n /**\n * Markdown source used for rendering and clipboard data.\n */\n content: string;\n /**\n * Replaces only the visible rendered content and never the copied source.\n */\n children?: ReactNode;\n /**\n * Replaces the localized error message shown when copying fails.\n */\n copyFailedMessage?: string;\n /**\n * The height of the content in its collapsed state.\n */\n collapsedHeight?: number;\n /**\n * Transforms the generated HTML before it is written to the clipboard.\n */\n transformClipboardHtml?: (html: string) => string;\n};\n\nconst CopyableContent: FC<CopyableContentProps> = ({\n appearance = CopyableContentAppearance.Default,\n content,\n children,\n copyFailedMessage,\n collapsedHeight,\n transformClipboardHtml,\n}) => {\n const rootRef = useRef<HTMLElement>(null);\n const actionGroupRef = useRef<HTMLDivElement>(null);\n const copyFeedbackTimeoutRef = useRef<number>();\n const isActionGroupSticky = useStickyActionState(rootRef, actionGroupRef);\n const [hasCopied, setHasCopied] = useState(false);\n\n const colorScheme = useColorScheme();\n const colorMode =\n colorScheme?.theme.colorMode === 'dark'\n ? CopyableContentColorMode.Dark\n : CopyableContentColorMode.Light;\n\n const defaultCopyButtonText = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.copy),\n });\n const defaultCopyFailedMessage = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.copyFailed),\n });\n const shareText = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.share),\n });\n\n const html = useMemo(() => formatStringToHtml(content).html, [content]);\n\n useEffect(\n () => () => {\n window.clearTimeout(copyFeedbackTimeoutRef.current);\n },\n [],\n );\n\n const showCopyFeedback = useCallback(() => {\n window.clearTimeout(copyFeedbackTimeoutRef.current);\n setHasCopied(true);\n\n copyFeedbackTimeoutRef.current = window.setTimeout(() => {\n setHasCopied(false);\n }, COPY_FEEDBACK_DURATION);\n }, []);\n\n const handleCopy = useCallback(async () => {\n try {\n await copyableContentToClipboard(content, transformClipboardHtml);\n showCopyFeedback();\n } catch {\n void createDialog({\n showCloseIcon: true,\n text: copyFailedMessage ?? defaultCopyFailedMessage,\n toastType: ToastType.ERROR,\n type: DialogType.TOAST,\n }).open();\n }\n }, [\n content,\n defaultCopyFailedMessage,\n copyFailedMessage,\n showCopyFeedback,\n transformClipboardHtml,\n ]);\n\n return (\n <StyledCopyableContent\n $appearance={appearance}\n $colorMode={colorMode}\n className=\"copyable-content\"\n ref={rootRef}\n >\n <StyledCopyableContentActions ref={actionGroupRef}>\n <StyledCopyableContentActionGroup>\n <StyledCopyableContentButton\n $colorMode={colorMode}\n $isSticky={isActionGroupSticky}\n aria-label={defaultCopyButtonText}\n onClick={() => {\n void handleCopy();\n }}\n type=\"button\"\n >\n <Icon icons={hasCopied ? ['fa fa-check'] : ['fa-light fa-copy']} />\n </StyledCopyableContentButton>\n <SharingContextMenu\n link={content}\n shouldShowCallingCodeAction={false}\n shouldShowCopyAction={false}\n shouldUseDefaultTriggerStyles={false}\n >\n <StyledCopyableContentButton\n $colorMode={colorMode}\n $isSticky={isActionGroupSticky}\n aria-label={shareText}\n type=\"button\"\n >\n <Icon icons={['fa fa-share-nodes']} />\n </StyledCopyableContentButton>\n </SharingContextMenu>\n </StyledCopyableContentActionGroup>\n </StyledCopyableContentActions>\n {typeof collapsedHeight === 'number' ? (\n <StyledCopyableContentTruncation>\n <Truncation collapsedHeight={collapsedHeight}>\n <StyledCopyableContentBody\n $colorMode={colorMode}\n $shouldShowPadding={false}\n >\n {children ?? <div dangerouslySetInnerHTML={{ __html: html }} />}\n </StyledCopyableContentBody>\n </Truncation>\n </StyledCopyableContentTruncation>\n ) : (\n <StyledCopyableContentBody $colorMode={colorMode}>\n {children ?? <div dangerouslySetInnerHTML={{ __html: html }} />}\n </StyledCopyableContentBody>\n )}\n </StyledCopyableContent>\n );\n};\n\nCopyableContent.displayName = 'CopyableContent';\n\nexport default CopyableContent;\n\nexport { CopyableContentAppearance } from './CopyableContent.types';\n"],"mappings":"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","ignoreList":[]}
@@ -89,6 +89,20 @@ describe('CopyableContent', () => {
89
89
  const item = write.mock.calls[0][0][0];
90
90
  await expect(readBlob(await item.getType('text/plain'))).resolves.toBe('source markdown');
91
91
  });
92
+ it('passes the clipboard HTML transformer to the copy action', async () => {
93
+ const write = vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();
94
+ const transformClipboardHtml = vi.fn(html => `${html}<p>Styled</p>`);
95
+ render(/*#__PURE__*/React.createElement(CopyableContent, {
96
+ content: "source markdown",
97
+ transformClipboardHtml: transformClipboardHtml
98
+ }));
99
+ fireEvent.click(screen.getByRole('button', {
100
+ name: 'Kopieren'
101
+ }));
102
+ await waitFor(() => expect(transformClipboardHtml).toHaveBeenCalledOnce());
103
+ const item = write.mock.calls[0][0][0];
104
+ await expect(readBlob(await item.getType('text/html'))).resolves.toContain('<p>Styled</p>');
105
+ });
92
106
  it('shows a checkmark after a successful copy', async () => {
93
107
  vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();
94
108
  const {
@@ -1 +1 @@
1
- {"version":3,"file":"CopyableContent.test.js","names":["fireEvent","render","screen","waitFor","React","describe","expect","it","vi","CopyableContent","mock","Textstring","textstring","fallback","TextstringProvider","children","ttsToITextString","useTextstringValue","resizeObserverCallbacks","ResizeObserverMock","constructor","callback","push","disconnect","observe","unobserve","stubGlobal","triggerResize","height","forEach","contentRect","default","icons","createElement","join","shouldShowCallingCodeAction","shouldShowCopyAction","importOriginal","createDialog","fn","open","readBlob","blob","Promise","resolve","reject","reader","FileReader","onerror","error","onload","result","readAsText","content","getByRole","name","toBeInTheDocument","toHaveAttribute","document","querySelector","not","write","spyOn","navigator","clipboard","mockResolvedValue","click","toHaveBeenCalled","item","calls","getType","resolves","toBe","container","collapsedHeight","Array","fill"],"sources":["../../../../src/components/copyable-content/CopyableContent.test.tsx"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-call */\nimport { fireEvent, render, screen, waitFor } from '@testing-library/react';\nimport React from 'react';\nimport { describe, expect, it, vi } from 'vitest';\nimport CopyableContent from './CopyableContent';\n\nvi.mock('@chayns-components/textstring', () => ({\n Textstring: ({ textstring }: { textstring: { fallback: string } }) => textstring.fallback,\n TextstringProvider: ({ children }: { children: React.ReactNode }) => children,\n ttsToITextString: (textstring: { fallback: string }) => textstring,\n useTextstringValue: ({ textstring }: { textstring: { fallback: string } }) =>\n textstring.fallback,\n}));\n\ntype ResizeObserverCallback = (entries: ResizeObserverEntry[]) => void;\n\nconst resizeObserverCallbacks: ResizeObserverCallback[] = [];\n\nclass ResizeObserverMock {\n constructor(callback: ResizeObserverCallback) {\n resizeObserverCallbacks.push(callback);\n }\n\n disconnect() {}\n\n observe() {}\n\n unobserve() {}\n}\n\nvi.stubGlobal('ResizeObserver', ResizeObserverMock);\n\nconst triggerResize = (height: number) => {\n resizeObserverCallbacks.forEach((callback) => {\n callback([\n {\n contentRect: { height } as DOMRectReadOnly,\n } as ResizeObserverEntry,\n ]);\n });\n};\n\nvi.mock('../icon/Icon', () => ({\n default: ({ icons }: { icons: string[] }) => <span data-icons={icons.join(' ')} />,\n}));\n\nvi.mock('../sharing-context-menu/SharingContextMenu', () => ({\n default: ({\n children,\n shouldShowCallingCodeAction,\n shouldShowCopyAction,\n }: {\n children: React.ReactNode;\n shouldShowCallingCodeAction?: boolean;\n shouldShowCopyAction?: boolean;\n }) => (\n <div\n data-calling-code-action={shouldShowCallingCodeAction}\n data-copy-action={shouldShowCopyAction}\n >\n {children}\n </div>\n ),\n}));\n\nvi.mock('chayns-api', async (importOriginal) => ({\n ...(await importOriginal<typeof import('chayns-api')>()),\n createDialog: vi.fn(() => ({ open: vi.fn() })),\n}));\n\nconst readBlob = (blob: Blob) =>\n new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = () => reject(reader.error);\n reader.onload = () => resolve(reader.result as string);\n reader.readAsText(blob);\n });\n\ndescribe('CopyableContent', () => {\n it('renders formatted markdown without active input HTML', () => {\n render(\n <CopyableContent\n content={\n '# Heading\\n\\n- Entry\\n\\n[Link](https://example.com)\\n<script onload=\"x\">bad</script>'\n }\n />,\n );\n\n expect(screen.getByRole('heading', { name: 'Heading' })).toBeInTheDocument();\n expect(screen.getByRole('link', { name: 'Link' })).toHaveAttribute(\n 'href',\n 'https://example.com',\n );\n expect(document.querySelector('script')).not.toBeInTheDocument();\n expect(document.querySelector('[onload]')).not.toBeInTheDocument();\n });\n\n it('copies content instead of custom children', async () => {\n const write = vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n render(<CopyableContent content=\"source markdown\">Visible replacement</CopyableContent>);\n\n fireEvent.click(screen.getByRole('button', { name: 'Kopieren' }));\n\n await waitFor(() => expect(write).toHaveBeenCalled());\n const item = write.mock.calls[0][0][0] as unknown as ClipboardItem;\n await expect(readBlob(await item.getType('text/plain'))).resolves.toBe('source markdown');\n });\n\n it('shows a checkmark after a successful copy', async () => {\n vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n const { container } = render(<CopyableContent content=\"source\" />);\n\n fireEvent.click(screen.getByRole('button', { name: 'Kopieren' }));\n\n await waitFor(() => {\n expect(container.querySelector('[data-icons=\"fa fa-check\"]')).toBeInTheDocument();\n });\n });\n\n it('offers sharing actions', () => {\n const { container } = render(<CopyableContent content=\"source\" />);\n\n expect(screen.getByRole('button', { name: 'Teilen' })).toBeInTheDocument();\n expect(container.querySelector('[data-copy-action=\"false\"]')).toBeInTheDocument();\n expect(container.querySelector('[data-calling-code-action=\"false\"]')).toBeInTheDocument();\n });\n\n it('supports collapsed content', async () => {\n render(\n <CopyableContent\n collapsedHeight={100}\n content={Array(20).fill('Long content').join('\\n\\n')}\n />,\n );\n\n triggerResize(240);\n\n await waitFor(() => {\n expect(screen.getByRole('button', { name: 'Mehr' })).toBeInTheDocument();\n });\n });\n});\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"CopyableContent.test.js","names":["fireEvent","render","screen","waitFor","React","describe","expect","it","vi","CopyableContent","mock","Textstring","textstring","fallback","TextstringProvider","children","ttsToITextString","useTextstringValue","resizeObserverCallbacks","ResizeObserverMock","constructor","callback","push","disconnect","observe","unobserve","stubGlobal","triggerResize","height","forEach","contentRect","default","icons","createElement","join","shouldShowCallingCodeAction","shouldShowCopyAction","importOriginal","createDialog","fn","open","readBlob","blob","Promise","resolve","reject","reader","FileReader","onerror","error","onload","result","readAsText","content","getByRole","name","toBeInTheDocument","toHaveAttribute","document","querySelector","not","write","spyOn","navigator","clipboard","mockResolvedValue","click","toHaveBeenCalled","item","calls","getType","resolves","toBe","transformClipboardHtml","html","toHaveBeenCalledOnce","toContain","container","collapsedHeight","Array","fill"],"sources":["../../../../src/components/copyable-content/CopyableContent.test.tsx"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-call */\nimport { fireEvent, render, screen, waitFor } from '@testing-library/react';\nimport React from 'react';\nimport { describe, expect, it, vi } from 'vitest';\nimport CopyableContent from './CopyableContent';\n\nvi.mock('@chayns-components/textstring', () => ({\n Textstring: ({ textstring }: { textstring: { fallback: string } }) => textstring.fallback,\n TextstringProvider: ({ children }: { children: React.ReactNode }) => children,\n ttsToITextString: (textstring: { fallback: string }) => textstring,\n useTextstringValue: ({ textstring }: { textstring: { fallback: string } }) =>\n textstring.fallback,\n}));\n\ntype ResizeObserverCallback = (entries: ResizeObserverEntry[]) => void;\n\nconst resizeObserverCallbacks: ResizeObserverCallback[] = [];\n\nclass ResizeObserverMock {\n constructor(callback: ResizeObserverCallback) {\n resizeObserverCallbacks.push(callback);\n }\n\n disconnect() {}\n\n observe() {}\n\n unobserve() {}\n}\n\nvi.stubGlobal('ResizeObserver', ResizeObserverMock);\n\nconst triggerResize = (height: number) => {\n resizeObserverCallbacks.forEach((callback) => {\n callback([\n {\n contentRect: { height } as DOMRectReadOnly,\n } as ResizeObserverEntry,\n ]);\n });\n};\n\nvi.mock('../icon/Icon', () => ({\n default: ({ icons }: { icons: string[] }) => <span data-icons={icons.join(' ')} />,\n}));\n\nvi.mock('../sharing-context-menu/SharingContextMenu', () => ({\n default: ({\n children,\n shouldShowCallingCodeAction,\n shouldShowCopyAction,\n }: {\n children: React.ReactNode;\n shouldShowCallingCodeAction?: boolean;\n shouldShowCopyAction?: boolean;\n }) => (\n <div\n data-calling-code-action={shouldShowCallingCodeAction}\n data-copy-action={shouldShowCopyAction}\n >\n {children}\n </div>\n ),\n}));\n\nvi.mock('chayns-api', async (importOriginal) => ({\n ...(await importOriginal<typeof import('chayns-api')>()),\n createDialog: vi.fn(() => ({ open: vi.fn() })),\n}));\n\nconst readBlob = (blob: Blob) =>\n new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = () => reject(reader.error);\n reader.onload = () => resolve(reader.result as string);\n reader.readAsText(blob);\n });\n\ndescribe('CopyableContent', () => {\n it('renders formatted markdown without active input HTML', () => {\n render(\n <CopyableContent\n content={\n '# Heading\\n\\n- Entry\\n\\n[Link](https://example.com)\\n<script onload=\"x\">bad</script>'\n }\n />,\n );\n\n expect(screen.getByRole('heading', { name: 'Heading' })).toBeInTheDocument();\n expect(screen.getByRole('link', { name: 'Link' })).toHaveAttribute(\n 'href',\n 'https://example.com',\n );\n expect(document.querySelector('script')).not.toBeInTheDocument();\n expect(document.querySelector('[onload]')).not.toBeInTheDocument();\n });\n\n it('copies content instead of custom children', async () => {\n const write = vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n render(<CopyableContent content=\"source markdown\">Visible replacement</CopyableContent>);\n\n fireEvent.click(screen.getByRole('button', { name: 'Kopieren' }));\n\n await waitFor(() => expect(write).toHaveBeenCalled());\n const item = write.mock.calls[0][0][0] as unknown as ClipboardItem;\n await expect(readBlob(await item.getType('text/plain'))).resolves.toBe('source markdown');\n });\n\n it('passes the clipboard HTML transformer to the copy action', async () => {\n const write = vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n const transformClipboardHtml = vi.fn((html: string) => `${html}<p>Styled</p>`);\n\n render(\n <CopyableContent\n content=\"source markdown\"\n transformClipboardHtml={transformClipboardHtml}\n />,\n );\n\n fireEvent.click(screen.getByRole('button', { name: 'Kopieren' }));\n\n await waitFor(() => expect(transformClipboardHtml).toHaveBeenCalledOnce());\n const item = write.mock.calls[0][0][0];\n\n await expect(readBlob(await item.getType('text/html'))).resolves.toContain('<p>Styled</p>');\n });\n\n it('shows a checkmark after a successful copy', async () => {\n vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n const { container } = render(<CopyableContent content=\"source\" />);\n\n fireEvent.click(screen.getByRole('button', { name: 'Kopieren' }));\n\n await waitFor(() => {\n expect(container.querySelector('[data-icons=\"fa fa-check\"]')).toBeInTheDocument();\n });\n });\n\n it('offers sharing actions', () => {\n const { container } = render(<CopyableContent content=\"source\" />);\n\n expect(screen.getByRole('button', { name: 'Teilen' })).toBeInTheDocument();\n expect(container.querySelector('[data-copy-action=\"false\"]')).toBeInTheDocument();\n expect(container.querySelector('[data-calling-code-action=\"false\"]')).toBeInTheDocument();\n });\n\n it('supports collapsed content', async () => {\n render(\n <CopyableContent\n collapsedHeight={100}\n content={Array(20).fill('Long content').join('\\n\\n')}\n />,\n );\n\n triggerResize(240);\n\n await waitFor(() => {\n expect(screen.getByRole('button', { name: 'Mehr' })).toBeInTheDocument();\n });\n });\n});\n"],"mappings":"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","ignoreList":[]}
@@ -8,13 +8,14 @@ const getPlainTextFromHtml = html => {
8
8
  return element.textContent?.trim() ?? '';
9
9
  }).filter(Boolean).join('\n\n');
10
10
  };
11
- const createClipboardContent = source => {
11
+ const createClipboardContent = (source, transformHtml) => {
12
12
  const {
13
13
  html
14
14
  } = formatStringToHtml(source);
15
+ const clipboardHtml = transformHtml?.(html) ?? html;
15
16
  return {
16
- html,
17
- plainText: getPlainTextFromHtml(html)
17
+ html: clipboardHtml,
18
+ plainText: getPlainTextFromHtml(clipboardHtml)
18
19
  };
19
20
  };
20
21
  const copyWithClipboardItem = async ({
@@ -46,8 +47,8 @@ const copyPlainText = async plainText => {
46
47
  }
47
48
  await navigator.clipboard.writeText(plainText);
48
49
  };
49
- export const copyableContentToClipboard = async source => {
50
- const clipboardContent = createClipboardContent(source);
50
+ export const copyableContentToClipboard = async (source, transformHtml) => {
51
+ const clipboardContent = createClipboardContent(source, transformHtml);
51
52
  if (!navigator.clipboard) {
52
53
  throw new Error('Clipboard API is not available.');
53
54
  }
@@ -1 +1 @@
1
- {"version":3,"file":"copyableContentClipboard.js","names":["formatStringToHtml","getPlainTextFromHtml","html","document","DOMParser","parseFromString","Array","from","body","children","map","element","tagName","item","textContent","trim","join","filter","Boolean","createClipboardContent","source","plainText","copyWithClipboardItem","clipboardData","ClipboardItem","Blob","type","navigator","clipboard","write","copyPlainText","writeText","copyableContentToClipboard","clipboardContent","Error"],"sources":["../../../../src/components/copyable-content/copyableContentClipboard.ts"],"sourcesContent":["import { formatStringToHtml } from '@chayns-components/format';\n\ninterface ClipboardContent {\n html: string;\n plainText: string;\n}\n\nconst getPlainTextFromHtml = (html: string) => {\n const document = new DOMParser().parseFromString(html, 'text/html');\n\n return Array.from(document.body.children)\n .map((element) => {\n if (element.tagName === 'UL' || element.tagName === 'OL') {\n return Array.from(element.children)\n .map((item) => `• ${item.textContent?.trim() ?? ''}`)\n .join('\\n');\n }\n\n return element.textContent?.trim() ?? '';\n })\n .filter(Boolean)\n .join('\\n\\n');\n};\n\nconst createClipboardContent = (source: string): ClipboardContent => {\n const { html } = formatStringToHtml(source);\n return { html, plainText: getPlainTextFromHtml(html) };\n};\n\nconst copyWithClipboardItem = async ({ html, plainText }: ClipboardContent) => {\n const clipboardData = new ClipboardItem({\n 'text/plain': new Blob([plainText], { type: 'text/plain' }),\n 'text/html': new Blob([html], { type: 'text/html' }),\n });\n\n await navigator.clipboard.write([clipboardData]);\n};\n\nconst copyPlainText = async (plainText: string) => {\n if (typeof ClipboardItem !== 'undefined') {\n try {\n await navigator.clipboard.write([\n new ClipboardItem({\n 'text/plain': new Blob([plainText], { type: 'text/plain' }),\n }),\n ]);\n return;\n } catch {\n // Use writeText as the final browser-compatible fallback.\n }\n }\n\n await navigator.clipboard.writeText(plainText);\n};\n\nexport const copyableContentToClipboard = async (source: string) => {\n const clipboardContent = createClipboardContent(source);\n\n if (!navigator.clipboard) {\n throw new Error('Clipboard API is not available.');\n }\n\n if (typeof ClipboardItem === 'undefined') {\n await copyPlainText(clipboardContent.plainText);\n return;\n }\n\n try {\n await copyWithClipboardItem(clipboardContent);\n } catch {\n // Use the plain-text fallback when the browser rejects rich MIME types.\n await copyPlainText(clipboardContent.plainText);\n }\n};\n"],"mappings":"AAAA,SAASA,kBAAkB,QAAQ,2BAA2B;AAO9D,MAAMC,oBAAoB,GAAIC,IAAY,IAAK;EAC3C,MAAMC,QAAQ,GAAG,IAAIC,SAAS,CAAC,CAAC,CAACC,eAAe,CAACH,IAAI,EAAE,WAAW,CAAC;EAEnE,OAAOI,KAAK,CAACC,IAAI,CAACJ,QAAQ,CAACK,IAAI,CAACC,QAAQ,CAAC,CACpCC,GAAG,CAAEC,OAAO,IAAK;IACd,IAAIA,OAAO,CAACC,OAAO,KAAK,IAAI,IAAID,OAAO,CAACC,OAAO,KAAK,IAAI,EAAE;MACtD,OAAON,KAAK,CAACC,IAAI,CAACI,OAAO,CAACF,QAAQ,CAAC,CAC9BC,GAAG,CAAEG,IAAI,IAAK,KAAKA,IAAI,CAACC,WAAW,EAAEC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACpDC,IAAI,CAAC,IAAI,CAAC;IACnB;IAEA,OAAOL,OAAO,CAACG,WAAW,EAAEC,IAAI,CAAC,CAAC,IAAI,EAAE;EAC5C,CAAC,CAAC,CACDE,MAAM,CAACC,OAAO,CAAC,CACfF,IAAI,CAAC,MAAM,CAAC;AACrB,CAAC;AAED,MAAMG,sBAAsB,GAAIC,MAAc,IAAuB;EACjE,MAAM;IAAElB;EAAK,CAAC,GAAGF,kBAAkB,CAACoB,MAAM,CAAC;EAC3C,OAAO;IAAElB,IAAI;IAAEmB,SAAS,EAAEpB,oBAAoB,CAACC,IAAI;EAAE,CAAC;AAC1D,CAAC;AAED,MAAMoB,qBAAqB,GAAG,MAAAA,CAAO;EAAEpB,IAAI;EAAEmB;AAA4B,CAAC,KAAK;EAC3E,MAAME,aAAa,GAAG,IAAIC,aAAa,CAAC;IACpC,YAAY,EAAE,IAAIC,IAAI,CAAC,CAACJ,SAAS,CAAC,EAAE;MAAEK,IAAI,EAAE;IAAa,CAAC,CAAC;IAC3D,WAAW,EAAE,IAAID,IAAI,CAAC,CAACvB,IAAI,CAAC,EAAE;MAAEwB,IAAI,EAAE;IAAY,CAAC;EACvD,CAAC,CAAC;EAEF,MAAMC,SAAS,CAACC,SAAS,CAACC,KAAK,CAAC,CAACN,aAAa,CAAC,CAAC;AACpD,CAAC;AAED,MAAMO,aAAa,GAAG,MAAOT,SAAiB,IAAK;EAC/C,IAAI,OAAOG,aAAa,KAAK,WAAW,EAAE;IACtC,IAAI;MACA,MAAMG,SAAS,CAACC,SAAS,CAACC,KAAK,CAAC,CAC5B,IAAIL,aAAa,CAAC;QACd,YAAY,EAAE,IAAIC,IAAI,CAAC,CAACJ,SAAS,CAAC,EAAE;UAAEK,IAAI,EAAE;QAAa,CAAC;MAC9D,CAAC,CAAC,CACL,CAAC;MACF;IACJ,CAAC,CAAC,MAAM;MACJ;IAAA;EAER;EAEA,MAAMC,SAAS,CAACC,SAAS,CAACG,SAAS,CAACV,SAAS,CAAC;AAClD,CAAC;AAED,OAAO,MAAMW,0BAA0B,GAAG,MAAOZ,MAAc,IAAK;EAChE,MAAMa,gBAAgB,GAAGd,sBAAsB,CAACC,MAAM,CAAC;EAEvD,IAAI,CAACO,SAAS,CAACC,SAAS,EAAE;IACtB,MAAM,IAAIM,KAAK,CAAC,iCAAiC,CAAC;EACtD;EAEA,IAAI,OAAOV,aAAa,KAAK,WAAW,EAAE;IACtC,MAAMM,aAAa,CAACG,gBAAgB,CAACZ,SAAS,CAAC;IAC/C;EACJ;EAEA,IAAI;IACA,MAAMC,qBAAqB,CAACW,gBAAgB,CAAC;EACjD,CAAC,CAAC,MAAM;IACJ;IACA,MAAMH,aAAa,CAACG,gBAAgB,CAACZ,SAAS,CAAC;EACnD;AACJ,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"copyableContentClipboard.js","names":["formatStringToHtml","getPlainTextFromHtml","html","document","DOMParser","parseFromString","Array","from","body","children","map","element","tagName","item","textContent","trim","join","filter","Boolean","createClipboardContent","source","transformHtml","clipboardHtml","plainText","copyWithClipboardItem","clipboardData","ClipboardItem","Blob","type","navigator","clipboard","write","copyPlainText","writeText","copyableContentToClipboard","clipboardContent","Error"],"sources":["../../../../src/components/copyable-content/copyableContentClipboard.ts"],"sourcesContent":["import { formatStringToHtml } from '@chayns-components/format';\n\ninterface ClipboardContent {\n html: string;\n plainText: string;\n}\n\nconst getPlainTextFromHtml = (html: string) => {\n const document = new DOMParser().parseFromString(html, 'text/html');\n\n return Array.from(document.body.children)\n .map((element) => {\n if (element.tagName === 'UL' || element.tagName === 'OL') {\n return Array.from(element.children)\n .map((item) => `• ${item.textContent?.trim() ?? ''}`)\n .join('\\n');\n }\n\n return element.textContent?.trim() ?? '';\n })\n .filter(Boolean)\n .join('\\n\\n');\n};\n\nconst createClipboardContent = (\n source: string,\n transformHtml?: (html: string) => string,\n): ClipboardContent => {\n const { html } = formatStringToHtml(source);\n const clipboardHtml = transformHtml?.(html) ?? html;\n\n return { html: clipboardHtml, plainText: getPlainTextFromHtml(clipboardHtml) };\n};\n\nconst copyWithClipboardItem = async ({ html, plainText }: ClipboardContent) => {\n const clipboardData = new ClipboardItem({\n 'text/plain': new Blob([plainText], { type: 'text/plain' }),\n 'text/html': new Blob([html], { type: 'text/html' }),\n });\n\n await navigator.clipboard.write([clipboardData]);\n};\n\nconst copyPlainText = async (plainText: string) => {\n if (typeof ClipboardItem !== 'undefined') {\n try {\n await navigator.clipboard.write([\n new ClipboardItem({\n 'text/plain': new Blob([plainText], { type: 'text/plain' }),\n }),\n ]);\n return;\n } catch {\n // Use writeText as the final browser-compatible fallback.\n }\n }\n\n await navigator.clipboard.writeText(plainText);\n};\n\nexport const copyableContentToClipboard = async (\n source: string,\n transformHtml?: (html: string) => string,\n) => {\n const clipboardContent = createClipboardContent(source, transformHtml);\n\n if (!navigator.clipboard) {\n throw new Error('Clipboard API is not available.');\n }\n\n if (typeof ClipboardItem === 'undefined') {\n await copyPlainText(clipboardContent.plainText);\n return;\n }\n\n try {\n await copyWithClipboardItem(clipboardContent);\n } catch {\n // Use the plain-text fallback when the browser rejects rich MIME types.\n await copyPlainText(clipboardContent.plainText);\n }\n};\n"],"mappings":"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","ignoreList":[]}
@@ -9,6 +9,12 @@ const readBlob = blob => new Promise((resolve, reject) => {
9
9
  reader.readAsText(blob);
10
10
  });
11
11
  describe('copyableContentToClipboard', () => {
12
+ it('transforms the generated HTML before copying', async () => {
13
+ const write = vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();
14
+ await copyableContentToClipboard('source', html => `${html}<p>Styled</p>`);
15
+ const clipboardItem = write.mock.calls[0][0][0];
16
+ await expect(readBlob(await clipboardItem.getType('text/html'))).resolves.toContain('<p>Styled</p>');
17
+ });
12
18
  afterEach(() => {
13
19
  vi.restoreAllMocks();
14
20
  vi.unstubAllGlobals();
@@ -1 +1 @@
1
- {"version":3,"file":"copyableContentClipboard.test.js","names":["formatStringToHtml","afterEach","describe","expect","it","vi","copyableContentToClipboard","source","readBlob","blob","Promise","resolve","reject","reader","FileReader","onerror","error","onload","result","readAsText","restoreAllMocks","unstubAllGlobals","write","spyOn","navigator","clipboard","mockResolvedValue","item","mock","calls","Object","keys","items","toEqual","getType","resolves","toBe","html","mockRejectedValueOnce","Error","mockResolvedValueOnce","writeText","toHaveBeenCalledTimes","not","toHaveBeenCalled","mockRejectedValue","toHaveBeenCalledWith","stubGlobal","undefined"],"sources":["../../../../src/components/copyable-content/copyableContentClipboard.test.ts"],"sourcesContent":["import { formatStringToHtml } from '@chayns-components/format';\nimport { afterEach, describe, expect, it, vi } from 'vitest';\nimport { copyableContentToClipboard } from './copyableContentClipboard';\n\nconst source = '# Heading\\n\\n[Link](https://example.com)';\n\nconst readBlob = (blob: Blob) =>\n new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = () => reject(reader.error);\n reader.onload = () => resolve(reader.result as string);\n reader.readAsText(blob);\n });\n\ndescribe('copyableContentToClipboard', () => {\n afterEach(() => {\n vi.restoreAllMocks();\n vi.unstubAllGlobals();\n });\n\n it('writes readable plain text first and safe HTML second', async () => {\n const write = vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n const item = write.mock.calls[0][0][0] as unknown as ClipboardItem & {\n items: Record<string, Blob>;\n };\n expect(Object.keys(item.items)).toEqual(['text/plain', 'text/html']);\n await expect(readBlob(await item.getType('text/plain'))).resolves.toBe('Heading\\n\\nLink');\n await expect(readBlob(await item.getType('text/html'))).resolves.toBe(\n formatStringToHtml(source).html,\n );\n });\n\n it('uses a plain ClipboardItem after a rejected rich write', async () => {\n const write = vi\n .spyOn(navigator.clipboard, 'write')\n .mockRejectedValueOnce(new Error('denied'))\n .mockResolvedValueOnce();\n const writeText = vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n expect(write).toHaveBeenCalledTimes(2);\n expect(writeText).not.toHaveBeenCalled();\n const item = write.mock.calls[1][0][0] as unknown as ClipboardItem & {\n items: Record<string, Blob>;\n };\n expect(Object.keys(item.items)).toEqual(['text/plain']);\n await expect(readBlob(await item.getType('text/plain'))).resolves.toBe('Heading\\n\\nLink');\n });\n\n it('uses writeText after all ClipboardItem writes fail', async () => {\n vi.spyOn(navigator.clipboard, 'write').mockRejectedValue(new Error('denied'));\n const writeText = vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n expect(writeText).toHaveBeenCalledWith('Heading\\n\\nLink');\n });\n\n it('uses writeText when ClipboardItem is unavailable', async () => {\n vi.stubGlobal('ClipboardItem', undefined);\n const writeText = vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n expect(writeText).toHaveBeenCalledWith('Heading\\n\\nLink');\n });\n});\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"copyableContentClipboard.test.js","names":["formatStringToHtml","afterEach","describe","expect","it","vi","copyableContentToClipboard","source","readBlob","blob","Promise","resolve","reject","reader","FileReader","onerror","error","onload","result","readAsText","write","spyOn","navigator","clipboard","mockResolvedValue","html","clipboardItem","mock","calls","getType","resolves","toContain","restoreAllMocks","unstubAllGlobals","item","Object","keys","items","toEqual","toBe","mockRejectedValueOnce","Error","mockResolvedValueOnce","writeText","toHaveBeenCalledTimes","not","toHaveBeenCalled","mockRejectedValue","toHaveBeenCalledWith","stubGlobal","undefined"],"sources":["../../../../src/components/copyable-content/copyableContentClipboard.test.ts"],"sourcesContent":["import { formatStringToHtml } from '@chayns-components/format';\nimport { afterEach, describe, expect, it, vi } from 'vitest';\nimport { copyableContentToClipboard } from './copyableContentClipboard';\n\nconst source = '# Heading\\n\\n[Link](https://example.com)';\n\nconst readBlob = (blob: Blob) =>\n new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = () => reject(reader.error);\n reader.onload = () => resolve(reader.result as string);\n reader.readAsText(blob);\n });\n\ndescribe('copyableContentToClipboard', () => {\n it('transforms the generated HTML before copying', async () => {\n const write = vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n\n await copyableContentToClipboard('source', (html) => `${html}<p>Styled</p>`);\n\n const clipboardItem = write.mock.calls[0][0][0];\n\n await expect(readBlob(await clipboardItem.getType('text/html'))).resolves.toContain(\n '<p>Styled</p>',\n );\n });\n\n afterEach(() => {\n vi.restoreAllMocks();\n vi.unstubAllGlobals();\n });\n\n it('writes readable plain text first and safe HTML second', async () => {\n const write = vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n const item = write.mock.calls[0][0][0] as unknown as ClipboardItem & {\n items: Record<string, Blob>;\n };\n expect(Object.keys(item.items)).toEqual(['text/plain', 'text/html']);\n await expect(readBlob(await item.getType('text/plain'))).resolves.toBe('Heading\\n\\nLink');\n await expect(readBlob(await item.getType('text/html'))).resolves.toBe(\n formatStringToHtml(source).html,\n );\n });\n\n it('uses a plain ClipboardItem after a rejected rich write', async () => {\n const write = vi\n .spyOn(navigator.clipboard, 'write')\n .mockRejectedValueOnce(new Error('denied'))\n .mockResolvedValueOnce();\n const writeText = vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n expect(write).toHaveBeenCalledTimes(2);\n expect(writeText).not.toHaveBeenCalled();\n const item = write.mock.calls[1][0][0] as unknown as ClipboardItem & {\n items: Record<string, Blob>;\n };\n expect(Object.keys(item.items)).toEqual(['text/plain']);\n await expect(readBlob(await item.getType('text/plain'))).resolves.toBe('Heading\\n\\nLink');\n });\n\n it('uses writeText after all ClipboardItem writes fail', async () => {\n vi.spyOn(navigator.clipboard, 'write').mockRejectedValue(new Error('denied'));\n const writeText = vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n expect(writeText).toHaveBeenCalledWith('Heading\\n\\nLink');\n });\n\n it('uses writeText when ClipboardItem is unavailable', async () => {\n vi.stubGlobal('ClipboardItem', undefined);\n const writeText = vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n expect(writeText).toHaveBeenCalledWith('Heading\\n\\nLink');\n });\n});\n"],"mappings":"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","ignoreList":[]}
@@ -21,6 +21,10 @@ export type CopyableContentProps = {
21
21
  * The height of the content in its collapsed state.
22
22
  */
23
23
  collapsedHeight?: number;
24
+ /**
25
+ * Transforms the generated HTML before it is written to the clipboard.
26
+ */
27
+ transformClipboardHtml?: (html: string) => string;
24
28
  };
25
29
  declare const CopyableContent: FC<CopyableContentProps>;
26
30
  export default CopyableContent;
@@ -1 +1 @@
1
- export declare const copyableContentToClipboard: (source: string) => Promise<void>;
1
+ export declare const copyableContentToClipboard: (source: string, transformHtml?: (html: string) => string) => Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chayns-components/core",
3
- "version": "5.5.28",
3
+ "version": "5.5.29",
4
4
  "description": "A set of beautiful React components for developing your own applications with chayns.",
5
5
  "sideEffects": false,
6
6
  "browserslist": [
@@ -93,5 +93,5 @@
93
93
  "publishConfig": {
94
94
  "access": "public"
95
95
  },
96
- "gitHead": "20b5354c0ee43c56e9919589f6b3bad7cf6b14bb"
96
+ "gitHead": "f8e901fa5342d0b0b294c12955bc68dddb31bcb8"
97
97
  }