@chayns-components/core 5.5.4 → 5.5.7
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 +87 -2
- package/lib/cjs/components/copyable-content/CopyableContent.js +102 -0
- package/lib/cjs/components/copyable-content/CopyableContent.js.map +1 -0
- package/lib/cjs/components/copyable-content/CopyableContent.styles.js +152 -0
- package/lib/cjs/components/copyable-content/CopyableContent.styles.js.map +1 -0
- package/lib/cjs/components/copyable-content/CopyableContent.test.js +83 -0
- package/lib/cjs/components/copyable-content/CopyableContent.test.js.map +1 -0
- package/lib/cjs/components/copyable-content/copyableContentClipboard.js +59 -0
- package/lib/cjs/components/copyable-content/copyableContentClipboard.js.map +1 -0
- package/lib/cjs/components/copyable-content/copyableContentClipboard.test.js +40 -0
- package/lib/cjs/components/copyable-content/copyableContentClipboard.test.js.map +1 -0
- package/lib/cjs/constants/textStrings.js +18 -0
- package/lib/cjs/constants/textStrings.js.map +1 -1
- package/lib/cjs/index.js +7 -0
- package/lib/cjs/index.js.map +1 -1
- package/lib/esm/components/copyable-content/CopyableContent.js +93 -0
- package/lib/esm/components/copyable-content/CopyableContent.js.map +1 -0
- package/lib/esm/components/copyable-content/CopyableContent.styles.js +145 -0
- package/lib/esm/components/copyable-content/CopyableContent.styles.js.map +1 -0
- package/lib/esm/components/copyable-content/CopyableContent.test.js +80 -0
- package/lib/esm/components/copyable-content/CopyableContent.test.js.map +1 -0
- package/lib/esm/components/copyable-content/copyableContentClipboard.js +52 -0
- package/lib/esm/components/copyable-content/copyableContentClipboard.js.map +1 -0
- package/lib/esm/components/copyable-content/copyableContentClipboard.test.js +38 -0
- package/lib/esm/components/copyable-content/copyableContentClipboard.test.js.map +1 -0
- package/lib/esm/constants/textStrings.js +18 -0
- package/lib/esm/constants/textStrings.js.map +1 -1
- package/lib/esm/index.js +1 -0
- package/lib/esm/index.js.map +1 -1
- package/lib/types/components/accordion/Accordion.styles.d.ts +3 -3
- package/lib/types/components/accordion/accordion-body/AccordionBody.styles.d.ts +3 -3
- package/lib/types/components/accordion/accordion-head/AccordionHead.styles.d.ts +30 -30
- package/lib/types/components/amount-control/AmountControl.styles.d.ts +3 -3
- package/lib/types/components/button/Button.styles.d.ts +9 -9
- package/lib/types/components/context-menu/context-menu-content/ContextMenuContent.styles.d.ts +3 -3
- package/lib/types/components/copyable-content/CopyableContent.d.ts +9 -0
- package/lib/types/components/copyable-content/CopyableContent.styles.d.ts +25 -0
- package/lib/types/components/copyable-content/copyableContentClipboard.d.ts +1 -0
- package/lib/types/components/dropdown-body-wrapper/delayed-dropdown-content/DelayedDropdownContent.styles.d.ts +3 -3
- package/lib/types/components/expandable-content/ExpandableContent.styles.d.ts +3 -3
- package/lib/types/components/file-input/FileInput.styles.d.ts +3 -3
- package/lib/types/components/file-select/FileSelect.styles.d.ts +3 -3
- package/lib/types/components/filter/Filter.styles.d.ts +3 -3
- package/lib/types/components/filter-buttons/filter-button/FilterButton.styles.d.ts +3 -3
- package/lib/types/components/highlight-slider/highlight-slider-item/HighlightSliderItem.styles.d.ts +6 -6
- package/lib/types/components/input/Input.styles.d.ts +9 -9
- package/lib/types/components/list/list-item/ListItem.styles.d.ts +4 -4
- package/lib/types/components/list/list-item/list-item-body/ListItemBody.styles.d.ts +3 -3
- package/lib/types/components/list/list-item/list-item-head/ListItemHead.styles.d.ts +11 -11
- package/lib/types/components/masonry/masonry-item/MasonryItem.styles.d.ts +3 -3
- package/lib/types/components/mention-finder/MentionFinder.styles.d.ts +6 -6
- package/lib/types/components/multi-action-button/MultiActionButton.styles.d.ts +3 -3
- package/lib/types/components/multi-action-button/action-button/ActionButton.styles.d.ts +3 -3
- package/lib/types/components/popup/popup-content-wrapper/PopupContentWrapper.styles.d.ts +3 -3
- package/lib/types/components/progress-bar/ProgressBar.styles.d.ts +3 -3
- package/lib/types/components/radio-button/RadioButton.styles.d.ts +3 -3
- package/lib/types/components/search-box/SearchBox.styles.d.ts +3 -3
- package/lib/types/components/search-input/SearchInput.styles.d.ts +6 -6
- package/lib/types/components/skeleton/base-skeleton/BaseSkeleton.styles.d.ts +6 -6
- package/lib/types/components/slider/Slider.styles.d.ts +5 -5
- package/lib/types/components/slider-button/SliderButton.styles.d.ts +3 -3
- package/lib/types/components/text-area/TextArea.styles.d.ts +3 -3
- package/lib/types/components/truncation/Truncation.styles.d.ts +3 -3
- package/lib/types/constants/textStrings.d.ts +18 -0
- package/lib/types/index.d.ts +1 -0
- package/package.json +11 -5
package/AGENTS.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @chayns-components/core
|
|
2
2
|
|
|
3
|
-
React component package providing
|
|
3
|
+
React component package providing 47 documented components for chayns applications.
|
|
4
4
|
|
|
5
|
-
Documented components: `Accordion`, `AmountControl`, `AnimatedNumber`, `Badge`, `Button`, `Checkbox`, `ComboBox`, `ContentCard`, `ContextMenu`, `ExpandableContent`, `FileInput`, `FileList`, `FileSelect`, `Filter`, `FilterButtons`, `GridImage`, `GroupedImage`, `HighlightSlider`, `Icon`, `Input`, `List`, `Masonry`, `MentionFinder`, `MultiActionButton`, `NumberInput`, `Popup`, `ProgressBar`, `RadioButton`, `ScrollView`, `SearchBox`, `SearchInput`, `SelectButton`, `SetupWizard`, `SetupWizardItem`, `SharingBar`, `SharingButton`, `Signature`, `Skeleton`, `Slider`, `SliderButton`, `SmallWaitCursor`, `TagInput`, `TextArea`, `Tooltip`, `Truncation`, `VerificationBadge`.
|
|
5
|
+
Documented components: `Accordion`, `AmountControl`, `AnimatedNumber`, `Badge`, `Button`, `Checkbox`, `ComboBox`, `ContentCard`, `ContextMenu`, `CopyableContent`, `ExpandableContent`, `FileInput`, `FileList`, `FileSelect`, `Filter`, `FilterButtons`, `GridImage`, `GroupedImage`, `HighlightSlider`, `Icon`, `Input`, `List`, `Masonry`, `MentionFinder`, `MultiActionButton`, `NumberInput`, `Popup`, `ProgressBar`, `RadioButton`, `ScrollView`, `SearchBox`, `SearchInput`, `SelectButton`, `SetupWizard`, `SetupWizardItem`, `SharingBar`, `SharingButton`, `Signature`, `Skeleton`, `Slider`, `SliderButton`, `SmallWaitCursor`, `TagInput`, `TextArea`, `Tooltip`, `Truncation`, `VerificationBadge`.
|
|
6
6
|
|
|
7
7
|
## Import
|
|
8
8
|
|
|
@@ -27,6 +27,7 @@ import { Accordion, AmountControl, AnimatedNumber } from '@chayns-components/cor
|
|
|
27
27
|
- `ComboBox`
|
|
28
28
|
- `ContentCard`
|
|
29
29
|
- `ContextMenu`
|
|
30
|
+
- `CopyableContent`
|
|
30
31
|
- `ExpandableContent`
|
|
31
32
|
- `FileInput`
|
|
32
33
|
- `FileList`
|
|
@@ -1463,6 +1464,90 @@ No additional exported types documented.
|
|
|
1463
1464
|
|
|
1464
1465
|
### Anti Patterns
|
|
1465
1466
|
|
|
1467
|
+
- Avoid imports from internal paths such as `@chayns-components/core/src/...`; always use the public package export.
|
|
1468
|
+
## CopyableContent
|
|
1469
|
+
|
|
1470
|
+
`CopyableContent` is exported by `@chayns-components/core` and should be imported from the public package entry point.
|
|
1471
|
+
|
|
1472
|
+
### Import
|
|
1473
|
+
|
|
1474
|
+
```ts
|
|
1475
|
+
import { CopyableContent } from '@chayns-components/core';
|
|
1476
|
+
```
|
|
1477
|
+
|
|
1478
|
+
### Examples
|
|
1479
|
+
|
|
1480
|
+
#### Short
|
|
1481
|
+
|
|
1482
|
+
```tsx
|
|
1483
|
+
<CopyableContent
|
|
1484
|
+
content={'Vielen Dank für eure Rückmeldungen. Der Termin für das gemeinsame Sommerfest steht nun fest: Samstag, 22. August, ab 15 Uhr.'}
|
|
1485
|
+
/>
|
|
1486
|
+
```
|
|
1487
|
+
|
|
1488
|
+
#### Long
|
|
1489
|
+
|
|
1490
|
+
```tsx
|
|
1491
|
+
<CopyableContent
|
|
1492
|
+
content={Array.from(
|
|
1493
|
+
{ length: 8 },
|
|
1494
|
+
(_, index) =>
|
|
1495
|
+
`### Abschnitt ${index + 1}\n\nDas Planungsteam hat die Hinweise aus den Gesprächen aufgenommen und bereitet jetzt die nächsten Schritte vor. Bis Ende der Woche sammeln wir noch Rückmeldungen, damit alle Beteiligten zuverlässig informiert sind.`,
|
|
1496
|
+
).join('\n\n')}
|
|
1497
|
+
/>
|
|
1498
|
+
```
|
|
1499
|
+
|
|
1500
|
+
#### Markdown
|
|
1501
|
+
|
|
1502
|
+
```tsx
|
|
1503
|
+
<CopyableContent
|
|
1504
|
+
content={`# Projekt-Update: Sommerfest
|
|
1505
|
+
|
|
1506
|
+
Die Vorbereitungen für das Sommerfest gehen in die letzte Runde. Das Organisationsteam hat die Rückmeldungen aus den einzelnen Gruppen zusammengeführt und den Ablauf für den Nachmittag angepasst.
|
|
1507
|
+
|
|
1508
|
+
## Was bereits feststeht
|
|
1509
|
+
|
|
1510
|
+
- Der Aufbau beginnt am Freitag um 16:30 Uhr am Bürgerhaus.
|
|
1511
|
+
- Für Kinder gibt es eine Kreativstation, eine kleine Rallye und einen ruhigen Rückzugsbereich.
|
|
1512
|
+
- Getränke und vegetarische Speisen werden vor Ort angeboten.
|
|
1513
|
+
|
|
1514
|
+
> Bitte gebt Rückmeldung, falls ihr beim Aufbau helfen könnt oder besondere Anforderungen an die Verpflegung habt.
|
|
1515
|
+
|
|
1516
|
+
Weitere Informationen stehen im [gemeinsamen Ablaufplan](https://example.com/veranstaltungen/sommerfest-2026/ablauf-und-helferinnen).`}
|
|
1517
|
+
/>
|
|
1518
|
+
```
|
|
1519
|
+
|
|
1520
|
+
#### Long Url
|
|
1521
|
+
|
|
1522
|
+
```tsx
|
|
1523
|
+
<CopyableContent
|
|
1524
|
+
content={'Die vollständige Materialliste findet ihr unter https://example.com/veranstaltungen/sommerfest-2026/organisation/materialien/helferinnen-und-helfer/abstimmung-und-zeitplan.'}
|
|
1525
|
+
/>
|
|
1526
|
+
```
|
|
1527
|
+
|
|
1528
|
+
#### Dark
|
|
1529
|
+
|
|
1530
|
+
```tsx
|
|
1531
|
+
<CopyableContent
|
|
1532
|
+
content={`${PROJECT_UPDATE}\n\n---\n\nDiese Story bitte mit dunklem Storybook-Hintergrund prüfen.`}
|
|
1533
|
+
/>
|
|
1534
|
+
```
|
|
1535
|
+
|
|
1536
|
+
### Props
|
|
1537
|
+
|
|
1538
|
+
No prop documentation available.
|
|
1539
|
+
|
|
1540
|
+
### Types
|
|
1541
|
+
|
|
1542
|
+
No additional exported types documented.
|
|
1543
|
+
|
|
1544
|
+
### Usage Notes
|
|
1545
|
+
|
|
1546
|
+
- Import `CopyableContent` directly from `@chayns-components/core` instead of internal source paths.
|
|
1547
|
+
- Start with one of the documented Storybook examples and adapt the props incrementally for your use case.
|
|
1548
|
+
|
|
1549
|
+
### Anti Patterns
|
|
1550
|
+
|
|
1466
1551
|
- Avoid imports from internal paths such as `@chayns-components/core/src/...`; always use the public package export.
|
|
1467
1552
|
## ExpandableContent
|
|
1468
1553
|
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _format = require("@chayns-components/format");
|
|
8
|
+
var _textstring = require("@chayns-components/textstring");
|
|
9
|
+
var _chaynsApi = require("chayns-api");
|
|
10
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
11
|
+
var _textStrings = _interopRequireDefault(require("../../constants/textStrings"));
|
|
12
|
+
var _SharingContextMenu = _interopRequireDefault(require("../sharing-context-menu/SharingContextMenu"));
|
|
13
|
+
var _Icon = _interopRequireDefault(require("../icon/Icon"));
|
|
14
|
+
var _CopyableContent = require("./CopyableContent.styles");
|
|
15
|
+
var _copyableContentClipboard = require("./copyableContentClipboard");
|
|
16
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
17
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
18
|
+
const STICKY_ACTION_OFFSET = 8;
|
|
19
|
+
const CopyableContent = ({
|
|
20
|
+
content,
|
|
21
|
+
children,
|
|
22
|
+
copiedMessage,
|
|
23
|
+
copyFailedMessage
|
|
24
|
+
}) => {
|
|
25
|
+
const [isActionGroupSticky, setIsActionGroupSticky] = (0, _react.useState)(false);
|
|
26
|
+
const actionGroupRef = (0, _react.useRef)(null);
|
|
27
|
+
const defaultCopyButtonText = (0, _textstring.useTextstringValue)({
|
|
28
|
+
textstring: (0, _textstring.ttsToITextString)(_textStrings.default.components.copyableContent.copy)
|
|
29
|
+
});
|
|
30
|
+
const defaultCopiedMessage = (0, _textstring.useTextstringValue)({
|
|
31
|
+
textstring: (0, _textstring.ttsToITextString)(_textStrings.default.components.copyableContent.copied)
|
|
32
|
+
});
|
|
33
|
+
const defaultCopyFailedMessage = (0, _textstring.useTextstringValue)({
|
|
34
|
+
textstring: (0, _textstring.ttsToITextString)(_textStrings.default.components.copyableContent.copyFailed)
|
|
35
|
+
});
|
|
36
|
+
const shareText = (0, _textstring.useTextstringValue)({
|
|
37
|
+
textstring: (0, _textstring.ttsToITextString)(_textStrings.default.components.copyableContent.share)
|
|
38
|
+
});
|
|
39
|
+
const html = (0, _react.useMemo)(() => (0, _format.formatStringToHtml)(content).html, [content]);
|
|
40
|
+
(0, _react.useEffect)(() => {
|
|
41
|
+
const updateStickyState = () => {
|
|
42
|
+
var _actionGroupRef$curre;
|
|
43
|
+
const actionGroupTop = (_actionGroupRef$curre = actionGroupRef.current) === null || _actionGroupRef$curre === void 0 ? void 0 : _actionGroupRef$curre.getBoundingClientRect().top;
|
|
44
|
+
setIsActionGroupSticky(actionGroupTop !== undefined && actionGroupTop <= STICKY_ACTION_OFFSET);
|
|
45
|
+
};
|
|
46
|
+
updateStickyState();
|
|
47
|
+
document.addEventListener('scroll', updateStickyState, true);
|
|
48
|
+
window.addEventListener('resize', updateStickyState);
|
|
49
|
+
return () => {
|
|
50
|
+
document.removeEventListener('scroll', updateStickyState, true);
|
|
51
|
+
window.removeEventListener('resize', updateStickyState);
|
|
52
|
+
};
|
|
53
|
+
}, []);
|
|
54
|
+
const handleCopy = (0, _react.useCallback)(async () => {
|
|
55
|
+
try {
|
|
56
|
+
await (0, _copyableContentClipboard.copyableContentToClipboard)(content);
|
|
57
|
+
void (0, _chaynsApi.createDialog)({
|
|
58
|
+
showCloseIcon: true,
|
|
59
|
+
text: copiedMessage ?? defaultCopiedMessage,
|
|
60
|
+
toastType: _chaynsApi.ToastType.SUCCESS,
|
|
61
|
+
type: _chaynsApi.DialogType.TOAST
|
|
62
|
+
}).open();
|
|
63
|
+
} catch {
|
|
64
|
+
void (0, _chaynsApi.createDialog)({
|
|
65
|
+
showCloseIcon: true,
|
|
66
|
+
text: copyFailedMessage ?? defaultCopyFailedMessage,
|
|
67
|
+
toastType: _chaynsApi.ToastType.ERROR,
|
|
68
|
+
type: _chaynsApi.DialogType.TOAST
|
|
69
|
+
}).open();
|
|
70
|
+
}
|
|
71
|
+
}, [content, copiedMessage, defaultCopiedMessage, defaultCopyFailedMessage, copyFailedMessage]);
|
|
72
|
+
return /*#__PURE__*/_react.default.createElement(_CopyableContent.StyledCopyableContent, {
|
|
73
|
+
className: "copyable-content"
|
|
74
|
+
}, /*#__PURE__*/_react.default.createElement(_CopyableContent.StyledCopyableContentActions, {
|
|
75
|
+
ref: actionGroupRef
|
|
76
|
+
}, /*#__PURE__*/_react.default.createElement(_CopyableContent.StyledCopyableContentActionGroup, null, /*#__PURE__*/_react.default.createElement(_CopyableContent.StyledCopyableContentButton, {
|
|
77
|
+
$isSticky: isActionGroupSticky,
|
|
78
|
+
"aria-label": defaultCopyButtonText,
|
|
79
|
+
onClick: () => {
|
|
80
|
+
void handleCopy();
|
|
81
|
+
},
|
|
82
|
+
type: "button"
|
|
83
|
+
}, /*#__PURE__*/_react.default.createElement(_Icon.default, {
|
|
84
|
+
icons: ['fa-light fa-copy']
|
|
85
|
+
})), /*#__PURE__*/_react.default.createElement(_SharingContextMenu.default, {
|
|
86
|
+
link: content,
|
|
87
|
+
shouldUseDefaultTriggerStyles: false
|
|
88
|
+
}, /*#__PURE__*/_react.default.createElement(_CopyableContent.StyledCopyableContentButton, {
|
|
89
|
+
$isSticky: isActionGroupSticky,
|
|
90
|
+
"aria-label": shareText,
|
|
91
|
+
type: "button"
|
|
92
|
+
}, /*#__PURE__*/_react.default.createElement(_Icon.default, {
|
|
93
|
+
icons: ['fa fa-share-nodes']
|
|
94
|
+
}))))), /*#__PURE__*/_react.default.createElement(_CopyableContent.StyledCopyableContentBody, null, children ?? /*#__PURE__*/_react.default.createElement("div", {
|
|
95
|
+
dangerouslySetInnerHTML: {
|
|
96
|
+
__html: html
|
|
97
|
+
}
|
|
98
|
+
})));
|
|
99
|
+
};
|
|
100
|
+
CopyableContent.displayName = 'CopyableContent';
|
|
101
|
+
var _default = exports.default = CopyableContent;
|
|
102
|
+
//# sourceMappingURL=CopyableContent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CopyableContent.js","names":["_format","require","_textstring","_chaynsApi","_react","_interopRequireWildcard","_textStrings","_interopRequireDefault","_SharingContextMenu","_Icon","_CopyableContent","_copyableContentClipboard","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","STICKY_ACTION_OFFSET","CopyableContent","content","children","copiedMessage","copyFailedMessage","isActionGroupSticky","setIsActionGroupSticky","useState","actionGroupRef","useRef","defaultCopyButtonText","useTextstringValue","textstring","ttsToITextString","textStrings","components","copyableContent","copy","defaultCopiedMessage","copied","defaultCopyFailedMessage","copyFailed","shareText","share","html","useMemo","formatStringToHtml","useEffect","updateStickyState","_actionGroupRef$curre","actionGroupTop","current","getBoundingClientRect","top","undefined","document","addEventListener","window","removeEventListener","handleCopy","useCallback","copyableContentToClipboard","createDialog","showCloseIcon","text","toastType","ToastType","SUCCESS","type","DialogType","TOAST","open","ERROR","createElement","StyledCopyableContent","className","StyledCopyableContentActions","ref","StyledCopyableContentActionGroup","StyledCopyableContentButton","$isSticky","onClick","icons","link","shouldUseDefaultTriggerStyles","StyledCopyableContentBody","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 SharingContextMenu from '../sharing-context-menu/SharingContextMenu';\nimport Icon from '../icon/Icon';\nimport {\n StyledCopyableContent,\n StyledCopyableContentActionGroup,\n StyledCopyableContentActions,\n StyledCopyableContentBody,\n StyledCopyableContentButton,\n} from './CopyableContent.styles';\nimport { copyableContentToClipboard } from './copyableContentClipboard';\n\nexport type CopyableContentProps = {\n content: string;\n children?: ReactNode;\n copiedMessage?: string;\n copyFailedMessage?: string;\n};\n\nconst STICKY_ACTION_OFFSET = 8;\n\nconst CopyableContent: FC<CopyableContentProps> = ({\n content,\n children,\n copiedMessage,\n copyFailedMessage,\n}) => {\n const [isActionGroupSticky, setIsActionGroupSticky] = useState(false);\n\n const actionGroupRef = useRef<HTMLDivElement>(null);\n\n const defaultCopyButtonText = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.copy),\n });\n const defaultCopiedMessage = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.copied),\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 const updateStickyState = () => {\n const actionGroupTop = actionGroupRef.current?.getBoundingClientRect().top;\n setIsActionGroupSticky(\n actionGroupTop !== undefined && actionGroupTop <= STICKY_ACTION_OFFSET,\n );\n };\n\n updateStickyState();\n document.addEventListener('scroll', updateStickyState, true);\n window.addEventListener('resize', updateStickyState);\n\n return () => {\n document.removeEventListener('scroll', updateStickyState, true);\n window.removeEventListener('resize', updateStickyState);\n };\n }, []);\n\n const handleCopy = useCallback(async () => {\n try {\n await copyableContentToClipboard(content);\n void createDialog({\n showCloseIcon: true,\n text: copiedMessage ?? defaultCopiedMessage,\n toastType: ToastType.SUCCESS,\n type: DialogType.TOAST,\n }).open();\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, copiedMessage, defaultCopiedMessage, defaultCopyFailedMessage, copyFailedMessage]);\n\n return (\n <StyledCopyableContent className=\"copyable-content\">\n <StyledCopyableContentActions ref={actionGroupRef}>\n <StyledCopyableContentActionGroup>\n <StyledCopyableContentButton\n $isSticky={isActionGroupSticky}\n aria-label={defaultCopyButtonText}\n onClick={() => {\n void handleCopy();\n }}\n type=\"button\"\n >\n <Icon icons={['fa-light fa-copy']} />\n </StyledCopyableContentButton>\n <SharingContextMenu link={content} shouldUseDefaultTriggerStyles={false}>\n <StyledCopyableContentButton\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 <StyledCopyableContentBody>\n {children ?? <div dangerouslySetInnerHTML={{ __html: html }} />}\n </StyledCopyableContentBody>\n </StyledCopyableContent>\n );\n};\n\nCopyableContent.displayName = 'CopyableContent';\n\nexport default CopyableContent;\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.StyledCopyableContentButton = exports.StyledCopyableContentBody = exports.StyledCopyableContentActions = exports.StyledCopyableContentActionGroup = exports.StyledCopyableContent = void 0;
|
|
7
|
+
var _styledComponents = _interopRequireDefault(require("styled-components"));
|
|
8
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
const StyledCopyableContent = exports.StyledCopyableContent = _styledComponents.default.section`
|
|
10
|
+
--copyable-content-action-size: 32px;
|
|
11
|
+
--copyable-content-action-inset: 8px;
|
|
12
|
+
|
|
13
|
+
min-width: 0;
|
|
14
|
+
max-width: 100%;
|
|
15
|
+
overflow-x: clip;
|
|
16
|
+
overflow-wrap: anywhere;
|
|
17
|
+
padding-top: calc(
|
|
18
|
+
var(--copyable-content-action-size) + var(--copyable-content-action-inset) * 2
|
|
19
|
+
);
|
|
20
|
+
border-radius: ${({
|
|
21
|
+
theme
|
|
22
|
+
}) => theme.cardBorderRadius}px;
|
|
23
|
+
background-color: rgba(
|
|
24
|
+
${({
|
|
25
|
+
theme
|
|
26
|
+
}) => theme['secondary-100-rgb']},
|
|
27
|
+
${({
|
|
28
|
+
theme
|
|
29
|
+
}) => theme.cardBackgroundOpacity}
|
|
30
|
+
);
|
|
31
|
+
color: ${({
|
|
32
|
+
theme
|
|
33
|
+
}) => theme.text};
|
|
34
|
+
`;
|
|
35
|
+
const StyledCopyableContentActions = exports.StyledCopyableContentActions = _styledComponents.default.div`
|
|
36
|
+
position: sticky;
|
|
37
|
+
top: var(--copyable-content-action-inset);
|
|
38
|
+
z-index: 1;
|
|
39
|
+
display: flex;
|
|
40
|
+
justify-content: flex-end;
|
|
41
|
+
height: calc(var(--copyable-content-action-size) + var(--copyable-content-action-inset));
|
|
42
|
+
margin-top: calc(
|
|
43
|
+
(var(--copyable-content-action-size) + var(--copyable-content-action-inset)) * -1
|
|
44
|
+
);
|
|
45
|
+
padding-right: var(--copyable-content-action-inset);
|
|
46
|
+
`;
|
|
47
|
+
const StyledCopyableContentActionGroup = exports.StyledCopyableContentActionGroup = _styledComponents.default.div`
|
|
48
|
+
display: flex;
|
|
49
|
+
gap: 4px;
|
|
50
|
+
height: var(--copyable-content-action-size);
|
|
51
|
+
`;
|
|
52
|
+
const StyledCopyableContentButton = exports.StyledCopyableContentButton = _styledComponents.default.button`
|
|
53
|
+
box-sizing: border-box;
|
|
54
|
+
border: 1px solid ${({
|
|
55
|
+
$isSticky,
|
|
56
|
+
theme
|
|
57
|
+
}) => $isSticky ? theme['202'] : 'transparent'};
|
|
58
|
+
border-radius: 4px;
|
|
59
|
+
width: var(--copyable-content-action-size);
|
|
60
|
+
height: var(--copyable-content-action-size);
|
|
61
|
+
padding: 0;
|
|
62
|
+
cursor: pointer;
|
|
63
|
+
background-color: ${({
|
|
64
|
+
$isSticky,
|
|
65
|
+
theme
|
|
66
|
+
}) => $isSticky ? theme['100'] : 'transparent'};
|
|
67
|
+
color: ${({
|
|
68
|
+
theme
|
|
69
|
+
}) => theme.text};
|
|
70
|
+
box-shadow: ${({
|
|
71
|
+
$isSticky
|
|
72
|
+
}) => $isSticky ? '0 2px 8px rgba(0, 0, 0, 0.16)' : 'none'};
|
|
73
|
+
transition:
|
|
74
|
+
background-color 0.15s ease,
|
|
75
|
+
border-color 0.15s ease,
|
|
76
|
+
box-shadow 0.15s ease,
|
|
77
|
+
transform 0.15s ease;
|
|
78
|
+
|
|
79
|
+
&:hover {
|
|
80
|
+
background-color: rgba(${({
|
|
81
|
+
theme
|
|
82
|
+
}) => theme['text-rgb']}, 0.1);
|
|
83
|
+
box-shadow: inset 0 0 0 1px rgba(${({
|
|
84
|
+
theme
|
|
85
|
+
}) => theme['text-rgb']}, 0.06);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
&:active {
|
|
89
|
+
transform: scale(0.9);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
&:focus-visible {
|
|
93
|
+
outline: 2px solid ${({
|
|
94
|
+
theme
|
|
95
|
+
}) => theme.buttonBackgroundColor};
|
|
96
|
+
outline-offset: 2px;
|
|
97
|
+
}
|
|
98
|
+
`;
|
|
99
|
+
const StyledCopyableContentBody = exports.StyledCopyableContentBody = _styledComponents.default.div`
|
|
100
|
+
min-width: 0;
|
|
101
|
+
max-width: 100%;
|
|
102
|
+
overflow-wrap: anywhere;
|
|
103
|
+
padding: 0 12px 12px;
|
|
104
|
+
|
|
105
|
+
h1,
|
|
106
|
+
h2,
|
|
107
|
+
h3,
|
|
108
|
+
h4,
|
|
109
|
+
h5,
|
|
110
|
+
h6,
|
|
111
|
+
p,
|
|
112
|
+
ul,
|
|
113
|
+
ol,
|
|
114
|
+
blockquote {
|
|
115
|
+
margin: 0 0 12px;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
h1,
|
|
119
|
+
h2,
|
|
120
|
+
h3,
|
|
121
|
+
h4,
|
|
122
|
+
h5,
|
|
123
|
+
h6 {
|
|
124
|
+
color: ${({
|
|
125
|
+
theme
|
|
126
|
+
}) => theme.headline};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
ul,
|
|
130
|
+
ol {
|
|
131
|
+
padding-left: 24px;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
blockquote {
|
|
135
|
+
padding-left: 12px;
|
|
136
|
+
border-left: 3px solid ${({
|
|
137
|
+
theme
|
|
138
|
+
}) => theme['202']};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
a {
|
|
142
|
+
color: ${({
|
|
143
|
+
theme
|
|
144
|
+
}) => theme.primary};
|
|
145
|
+
overflow-wrap: anywhere;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
> :last-child {
|
|
149
|
+
margin-bottom: 0;
|
|
150
|
+
}
|
|
151
|
+
`;
|
|
152
|
+
//# sourceMappingURL=CopyableContent.styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CopyableContent.styles.js","names":["_styledComponents","_interopRequireDefault","require","e","__esModule","default","StyledCopyableContent","exports","styled","section","theme","cardBorderRadius","cardBackgroundOpacity","text","StyledCopyableContentActions","div","StyledCopyableContentActionGroup","StyledCopyableContentButton","button","$isSticky","buttonBackgroundColor","StyledCopyableContentBody","headline","primary"],"sources":["../../../../src/components/copyable-content/CopyableContent.styles.ts"],"sourcesContent":["import type { WithTheme } from '../color-scheme-provider/ColorSchemeProvider';\nimport styled from 'styled-components';\n\ntype StyledCopyableContentProps = WithTheme<unknown>;\n\nexport const StyledCopyableContent = styled.section<StyledCopyableContentProps>`\n --copyable-content-action-size: 32px;\n --copyable-content-action-inset: 8px;\n\n min-width: 0;\n max-width: 100%;\n overflow-x: clip;\n overflow-wrap: anywhere;\n padding-top: calc(\n var(--copyable-content-action-size) + var(--copyable-content-action-inset) * 2\n );\n border-radius: ${({ theme }) => theme.cardBorderRadius}px;\n background-color: rgba(\n ${({ theme }) => theme['secondary-100-rgb']},\n ${({ theme }) => theme.cardBackgroundOpacity}\n );\n color: ${({ theme }) => theme.text};\n`;\n\ntype StyledCopyableContentButtonProps = WithTheme<{\n $isSticky: boolean;\n}>;\n\nexport const StyledCopyableContentActions = styled.div<StyledCopyableContentProps>`\n position: sticky;\n top: var(--copyable-content-action-inset);\n z-index: 1;\n display: flex;\n justify-content: flex-end;\n height: calc(var(--copyable-content-action-size) + var(--copyable-content-action-inset));\n margin-top: calc(\n (var(--copyable-content-action-size) + var(--copyable-content-action-inset)) * -1\n );\n padding-right: var(--copyable-content-action-inset);\n`;\n\nexport const StyledCopyableContentActionGroup = styled.div`\n display: flex;\n gap: 4px;\n height: var(--copyable-content-action-size);\n`;\n\nexport const StyledCopyableContentButton = styled.button<StyledCopyableContentButtonProps>`\n box-sizing: border-box;\n border: 1px solid ${({ $isSticky, theme }) => ($isSticky ? theme['202'] : 'transparent')};\n border-radius: 4px;\n width: var(--copyable-content-action-size);\n height: var(--copyable-content-action-size);\n padding: 0;\n cursor: pointer;\n background-color: ${({ $isSticky, theme }) => ($isSticky ? theme['100'] : 'transparent')};\n color: ${({ theme }) => theme.text};\n box-shadow: ${({ $isSticky }) => ($isSticky ? '0 2px 8px rgba(0, 0, 0, 0.16)' : 'none')};\n transition:\n background-color 0.15s ease,\n border-color 0.15s ease,\n box-shadow 0.15s ease,\n transform 0.15s ease;\n\n &:hover {\n background-color: rgba(${({ theme }) => theme['text-rgb']}, 0.1);\n box-shadow: inset 0 0 0 1px rgba(${({ theme }) => theme['text-rgb']}, 0.06);\n }\n\n &:active {\n transform: scale(0.9);\n }\n\n &:focus-visible {\n outline: 2px solid ${({ theme }) => theme.buttonBackgroundColor};\n outline-offset: 2px;\n }\n`;\n\nexport const StyledCopyableContentBody = styled.div<StyledCopyableContentProps>`\n min-width: 0;\n max-width: 100%;\n overflow-wrap: anywhere;\n padding: 0 12px 12px;\n\n h1,\n h2,\n h3,\n h4,\n h5,\n h6,\n p,\n ul,\n ol,\n blockquote {\n margin: 0 0 12px;\n }\n\n h1,\n h2,\n h3,\n h4,\n h5,\n h6 {\n color: ${({ theme }) => theme.headline};\n }\n\n ul,\n ol {\n padding-left: 24px;\n }\n\n blockquote {\n padding-left: 12px;\n border-left: 3px solid ${({ theme }) => theme['202']};\n }\n\n a {\n color: ${({ theme }) => theme.primary};\n overflow-wrap: anywhere;\n }\n\n > :last-child {\n margin-bottom: 0;\n }\n`;\n"],"mappings":";;;;;;AACA,IAAAA,iBAAA,GAAAC,sBAAA,CAAAC,OAAA;AAAuC,SAAAD,uBAAAE,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAIhC,MAAMG,qBAAqB,GAAAC,OAAA,CAAAD,qBAAA,GAAGE,yBAAM,CAACC,OAAmC;AAC/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB,CAAC;EAAEC;AAAM,CAAC,KAAKA,KAAK,CAACC,gBAAgB;AAC1D;AACA,UAAU,CAAC;EAAED;AAAM,CAAC,KAAKA,KAAK,CAAC,mBAAmB,CAAC;AACnD,UAAU,CAAC;EAAEA;AAAM,CAAC,KAAKA,KAAK,CAACE,qBAAqB;AACpD;AACA,aAAa,CAAC;EAAEF;AAAM,CAAC,KAAKA,KAAK,CAACG,IAAI;AACtC,CAAC;AAMM,MAAMC,4BAA4B,GAAAP,OAAA,CAAAO,4BAAA,GAAGN,yBAAM,CAACO,GAA+B;AAClF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AAEM,MAAMC,gCAAgC,GAAAT,OAAA,CAAAS,gCAAA,GAAGR,yBAAM,CAACO,GAAG;AAC1D;AACA;AACA;AACA,CAAC;AAEM,MAAME,2BAA2B,GAAAV,OAAA,CAAAU,2BAAA,GAAGT,yBAAM,CAACU,MAAwC;AAC1F;AACA,wBAAwB,CAAC;EAAEC,SAAS;EAAET;AAAM,CAAC,KAAMS,SAAS,GAAGT,KAAK,CAAC,KAAK,CAAC,GAAG,aAAc;AAC5F;AACA;AACA;AACA;AACA;AACA,wBAAwB,CAAC;EAAES,SAAS;EAAET;AAAM,CAAC,KAAMS,SAAS,GAAGT,KAAK,CAAC,KAAK,CAAC,GAAG,aAAc;AAC5F,aAAa,CAAC;EAAEA;AAAM,CAAC,KAAKA,KAAK,CAACG,IAAI;AACtC,kBAAkB,CAAC;EAAEM;AAAU,CAAC,KAAMA,SAAS,GAAG,+BAA+B,GAAG,MAAO;AAC3F;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iCAAiC,CAAC;EAAET;AAAM,CAAC,KAAKA,KAAK,CAAC,UAAU,CAAC;AACjE,2CAA2C,CAAC;EAAEA;AAAM,CAAC,KAAKA,KAAK,CAAC,UAAU,CAAC;AAC3E;AACA;AACA;AACA;AACA;AACA;AACA;AACA,6BAA6B,CAAC;EAAEA;AAAM,CAAC,KAAKA,KAAK,CAACU,qBAAqB;AACvE;AACA;AACA,CAAC;AAEM,MAAMC,yBAAyB,GAAAd,OAAA,CAAAc,yBAAA,GAAGb,yBAAM,CAACO,GAA+B;AAC/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,CAAC;EAAEL;AAAM,CAAC,KAAKA,KAAK,CAACY,QAAQ;AAC9C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iCAAiC,CAAC;EAAEZ;AAAM,CAAC,KAAKA,KAAK,CAAC,KAAK,CAAC;AAC5D;AACA;AACA;AACA,iBAAiB,CAAC;EAAEA;AAAM,CAAC,KAAKA,KAAK,CAACa,OAAO;AAC7C;AACA;AACA;AACA;AACA;AACA;AACA,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _react = require("@testing-library/react");
|
|
4
|
+
var _react2 = _interopRequireDefault(require("react"));
|
|
5
|
+
var _vitest = require("vitest");
|
|
6
|
+
var _CopyableContent = _interopRequireDefault(require("./CopyableContent"));
|
|
7
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } /* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
9
|
+
_vitest.vi.mock('../icon/Icon', () => ({
|
|
10
|
+
default: () => /*#__PURE__*/_react2.default.createElement("span", null)
|
|
11
|
+
}));
|
|
12
|
+
_vitest.vi.mock('../sharing-context-menu/SharingContextMenu', () => ({
|
|
13
|
+
default: ({
|
|
14
|
+
children
|
|
15
|
+
}) => children
|
|
16
|
+
}));
|
|
17
|
+
_vitest.vi.mock('chayns-api', async importOriginal => ({
|
|
18
|
+
...(await importOriginal()),
|
|
19
|
+
createDialog: _vitest.vi.fn(() => ({
|
|
20
|
+
open: _vitest.vi.fn()
|
|
21
|
+
}))
|
|
22
|
+
}));
|
|
23
|
+
const readBlob = blob => new Promise((resolve, reject) => {
|
|
24
|
+
const reader = new FileReader();
|
|
25
|
+
reader.onerror = () => reject(reader.error);
|
|
26
|
+
reader.onload = () => resolve(reader.result);
|
|
27
|
+
reader.readAsText(blob);
|
|
28
|
+
});
|
|
29
|
+
(0, _vitest.describe)('CopyableContent', () => {
|
|
30
|
+
(0, _vitest.it)('renders formatted markdown without active input HTML', () => {
|
|
31
|
+
(0, _react.render)(/*#__PURE__*/_react2.default.createElement(_CopyableContent.default, {
|
|
32
|
+
content: '# Heading\n\n- Entry\n\n[Link](https://example.com)\n<script onload="x">bad</script>'
|
|
33
|
+
}));
|
|
34
|
+
(0, _vitest.expect)(_react.screen.getByRole('heading', {
|
|
35
|
+
name: 'Heading'
|
|
36
|
+
})).toBeInTheDocument();
|
|
37
|
+
(0, _vitest.expect)(_react.screen.getByRole('link', {
|
|
38
|
+
name: 'Link'
|
|
39
|
+
})).toHaveAttribute('href', 'https://example.com');
|
|
40
|
+
(0, _vitest.expect)(document.querySelector('script')).not.toBeInTheDocument();
|
|
41
|
+
(0, _vitest.expect)(document.querySelector('[onload]')).not.toBeInTheDocument();
|
|
42
|
+
});
|
|
43
|
+
(0, _vitest.it)('copies content instead of custom children', async () => {
|
|
44
|
+
const write = _vitest.vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();
|
|
45
|
+
(0, _react.render)(/*#__PURE__*/_react2.default.createElement(_CopyableContent.default, {
|
|
46
|
+
content: "source markdown"
|
|
47
|
+
}, "Visible replacement"));
|
|
48
|
+
_react.fireEvent.click(_react.screen.getByRole('button', {
|
|
49
|
+
name: 'Kopieren'
|
|
50
|
+
}));
|
|
51
|
+
await (0, _react.waitFor)(() => (0, _vitest.expect)(write).toHaveBeenCalled());
|
|
52
|
+
const item = write.mock.calls[0][0][0];
|
|
53
|
+
await (0, _vitest.expect)(readBlob(await item.getType('text/plain'))).resolves.toBe('source markdown');
|
|
54
|
+
});
|
|
55
|
+
(0, _vitest.it)('shows a toast after a successful copy', async () => {
|
|
56
|
+
_vitest.vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();
|
|
57
|
+
(0, _react.render)(/*#__PURE__*/_react2.default.createElement(_CopyableContent.default, {
|
|
58
|
+
content: "source",
|
|
59
|
+
copiedMessage: "Done"
|
|
60
|
+
}));
|
|
61
|
+
_react.fireEvent.click(_react.screen.getByRole('button', {
|
|
62
|
+
name: 'Kopieren'
|
|
63
|
+
}));
|
|
64
|
+
await (0, _react.waitFor)(async () => {
|
|
65
|
+
const {
|
|
66
|
+
createDialog
|
|
67
|
+
} = await Promise.resolve().then(() => _interopRequireWildcard(require('chayns-api')));
|
|
68
|
+
(0, _vitest.expect)(createDialog).toHaveBeenCalledWith(_vitest.expect.objectContaining({
|
|
69
|
+
text: 'Done'
|
|
70
|
+
}));
|
|
71
|
+
});
|
|
72
|
+
(0, _vitest.expect)(_react.screen.queryByRole('status')).not.toBeInTheDocument();
|
|
73
|
+
});
|
|
74
|
+
(0, _vitest.it)('offers sharing actions', () => {
|
|
75
|
+
(0, _react.render)(/*#__PURE__*/_react2.default.createElement(_CopyableContent.default, {
|
|
76
|
+
content: "source"
|
|
77
|
+
}));
|
|
78
|
+
(0, _vitest.expect)(_react.screen.getByRole('button', {
|
|
79
|
+
name: 'Teilen'
|
|
80
|
+
})).toBeInTheDocument();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=CopyableContent.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CopyableContent.test.js","names":["_react","require","_react2","_interopRequireDefault","_vitest","_CopyableContent","e","__esModule","default","_interopRequireWildcard","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","vi","mock","createElement","children","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","copiedMessage","then","toHaveBeenCalledWith","objectContaining","text","queryByRole"],"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('../icon/Icon', () => ({\n default: () => <span />,\n}));\n\nvi.mock('../sharing-context-menu/SharingContextMenu', () => ({\n default: ({ children }: { children: React.ReactNode }) => children,\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 toast after a successful copy', async () => {\n vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n render(<CopyableContent content=\"source\" copiedMessage=\"Done\" />);\n\n fireEvent.click(screen.getByRole('button', { name: 'Kopieren' }));\n\n await waitFor(async () => {\n const { createDialog } = await import('chayns-api');\n expect(createDialog).toHaveBeenCalledWith(expect.objectContaining({ text: 'Done' }));\n });\n expect(screen.queryByRole('status')).not.toBeInTheDocument();\n });\n\n it('offers sharing actions', () => {\n render(<CopyableContent content=\"source\" />);\n\n expect(screen.getByRole('button', { name: 'Teilen' })).toBeInTheDocument();\n });\n});\n"],"mappings":";;AACA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,OAAA,GAAAC,sBAAA,CAAAF,OAAA;AACA,IAAAG,OAAA,GAAAH,OAAA;AACA,IAAAI,gBAAA,GAAAF,sBAAA,CAAAF,OAAA;AAAgD,SAAAE,uBAAAG,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAAA,SAAAG,wBAAAH,CAAA,EAAAI,CAAA,6BAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAAF,uBAAA,YAAAA,CAAAH,CAAA,EAAAI,CAAA,SAAAA,CAAA,IAAAJ,CAAA,IAAAA,CAAA,CAAAC,UAAA,SAAAD,CAAA,MAAAQ,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,QAAAT,OAAA,EAAAF,CAAA,iBAAAA,CAAA,uBAAAA,CAAA,yBAAAA,CAAA,SAAAU,CAAA,MAAAF,CAAA,GAAAJ,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAE,CAAA,CAAAI,GAAA,CAAAZ,CAAA,UAAAQ,CAAA,CAAAK,GAAA,CAAAb,CAAA,GAAAQ,CAAA,CAAAM,GAAA,CAAAd,CAAA,EAAAU,CAAA,gBAAAN,CAAA,IAAAJ,CAAA,gBAAAI,CAAA,OAAAW,cAAA,CAAAC,IAAA,CAAAhB,CAAA,EAAAI,CAAA,OAAAK,CAAA,IAAAD,CAAA,GAAAS,MAAA,CAAAC,cAAA,KAAAD,MAAA,CAAAE,wBAAA,CAAAnB,CAAA,EAAAI,CAAA,OAAAK,CAAA,CAAAI,GAAA,IAAAJ,CAAA,CAAAK,GAAA,IAAAN,CAAA,CAAAE,CAAA,EAAAN,CAAA,EAAAK,CAAA,IAAAC,CAAA,CAAAN,CAAA,IAAAJ,CAAA,CAAAI,CAAA,WAAAM,CAAA,KAAAV,CAAA,EAAAI,CAAA,KAJhD;AAMAgB,UAAE,CAACC,IAAI,CAAC,cAAc,EAAE,OAAO;EAC3BnB,OAAO,EAAEA,CAAA,kBAAMN,OAAA,CAAAM,OAAA,CAAAoB,aAAA,aAAO;AAC1B,CAAC,CAAC,CAAC;AAEHF,UAAE,CAACC,IAAI,CAAC,4CAA4C,EAAE,OAAO;EACzDnB,OAAO,EAAEA,CAAC;IAAEqB;EAAwC,CAAC,KAAKA;AAC9D,CAAC,CAAC,CAAC;AAEHH,UAAE,CAACC,IAAI,CAAC,YAAY,EAAE,MAAOG,cAAc,KAAM;EAC7C,IAAI,MAAMA,cAAc,CAA8B,CAAC,CAAC;EACxDC,YAAY,EAAEL,UAAE,CAACM,EAAE,CAAC,OAAO;IAAEC,IAAI,EAAEP,UAAE,CAACM,EAAE,CAAC;EAAE,CAAC,CAAC;AACjD,CAAC,CAAC,CAAC;AAEH,MAAME,QAAQ,GAAIC,IAAU,IACxB,IAAIC,OAAO,CAAS,CAACC,OAAO,EAAEC,MAAM,KAAK;EACrC,MAAMC,MAAM,GAAG,IAAIC,UAAU,CAAC,CAAC;EAC/BD,MAAM,CAACE,OAAO,GAAG,MAAMH,MAAM,CAACC,MAAM,CAACG,KAAK,CAAC;EAC3CH,MAAM,CAACI,MAAM,GAAG,MAAMN,OAAO,CAACE,MAAM,CAACK,MAAgB,CAAC;EACtDL,MAAM,CAACM,UAAU,CAACV,IAAI,CAAC;AAC3B,CAAC,CAAC;AAEN,IAAAW,gBAAQ,EAAC,iBAAiB,EAAE,MAAM;EAC9B,IAAAC,UAAE,EAAC,sDAAsD,EAAE,MAAM;IAC7D,IAAAC,aAAM,eACF9C,OAAA,CAAAM,OAAA,CAAAoB,aAAA,CAACvB,gBAAA,CAAAG,OAAe;MACZyC,OAAO,EACH;IACH,CACJ,CACL,CAAC;IAED,IAAAC,cAAM,EAACC,aAAM,CAACC,SAAS,CAAC,SAAS,EAAE;MAAEC,IAAI,EAAE;IAAU,CAAC,CAAC,CAAC,CAACC,iBAAiB,CAAC,CAAC;IAC5E,IAAAJ,cAAM,EAACC,aAAM,CAACC,SAAS,CAAC,MAAM,EAAE;MAAEC,IAAI,EAAE;IAAO,CAAC,CAAC,CAAC,CAACE,eAAe,CAC9D,MAAM,EACN,qBACJ,CAAC;IACD,IAAAL,cAAM,EAACM,QAAQ,CAACC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAACC,GAAG,CAACJ,iBAAiB,CAAC,CAAC;IAChE,IAAAJ,cAAM,EAACM,QAAQ,CAACC,aAAa,CAAC,UAAU,CAAC,CAAC,CAACC,GAAG,CAACJ,iBAAiB,CAAC,CAAC;EACtE,CAAC,CAAC;EAEF,IAAAP,UAAE,EAAC,2CAA2C,EAAE,YAAY;IACxD,MAAMY,KAAK,GAAGjC,UAAE,CAACkC,KAAK,CAACC,SAAS,CAACC,SAAS,EAAE,OAAO,CAAC,CAACC,iBAAiB,CAAC,CAAC;IACxE,IAAAf,aAAM,eAAC9C,OAAA,CAAAM,OAAA,CAAAoB,aAAA,CAACvB,gBAAA,CAAAG,OAAe;MAACyC,OAAO,EAAC;IAAiB,GAAC,qBAAoC,CAAC,CAAC;IAExFe,gBAAS,CAACC,KAAK,CAACd,aAAM,CAACC,SAAS,CAAC,QAAQ,EAAE;MAAEC,IAAI,EAAE;IAAW,CAAC,CAAC,CAAC;IAEjE,MAAM,IAAAa,cAAO,EAAC,MAAM,IAAAhB,cAAM,EAACS,KAAK,CAAC,CAACQ,gBAAgB,CAAC,CAAC,CAAC;IACrD,MAAMC,IAAI,GAAGT,KAAK,CAAChC,IAAI,CAAC0C,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAA6B;IAClE,MAAM,IAAAnB,cAAM,EAAChB,QAAQ,CAAC,MAAMkC,IAAI,CAACE,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAACC,QAAQ,CAACC,IAAI,CAAC,iBAAiB,CAAC;EAC7F,CAAC,CAAC;EAEF,IAAAzB,UAAE,EAAC,uCAAuC,EAAE,YAAY;IACpDrB,UAAE,CAACkC,KAAK,CAACC,SAAS,CAACC,SAAS,EAAE,OAAO,CAAC,CAACC,iBAAiB,CAAC,CAAC;IAC1D,IAAAf,aAAM,eAAC9C,OAAA,CAAAM,OAAA,CAAAoB,aAAA,CAACvB,gBAAA,CAAAG,OAAe;MAACyC,OAAO,EAAC,QAAQ;MAACwB,aAAa,EAAC;IAAM,CAAE,CAAC,CAAC;IAEjET,gBAAS,CAACC,KAAK,CAACd,aAAM,CAACC,SAAS,CAAC,QAAQ,EAAE;MAAEC,IAAI,EAAE;IAAW,CAAC,CAAC,CAAC;IAEjE,MAAM,IAAAa,cAAO,EAAC,YAAY;MACtB,MAAM;QAAEnC;MAAa,CAAC,GAAG,MAAAK,OAAA,CAAAC,OAAA,GAAAqC,IAAA,OAAAjE,uBAAA,CAAAR,OAAA,CAAa,YAAY,GAAC;MACnD,IAAAiD,cAAM,EAACnB,YAAY,CAAC,CAAC4C,oBAAoB,CAACzB,cAAM,CAAC0B,gBAAgB,CAAC;QAAEC,IAAI,EAAE;MAAO,CAAC,CAAC,CAAC;IACxF,CAAC,CAAC;IACF,IAAA3B,cAAM,EAACC,aAAM,CAAC2B,WAAW,CAAC,QAAQ,CAAC,CAAC,CAACpB,GAAG,CAACJ,iBAAiB,CAAC,CAAC;EAChE,CAAC,CAAC;EAEF,IAAAP,UAAE,EAAC,wBAAwB,EAAE,MAAM;IAC/B,IAAAC,aAAM,eAAC9C,OAAA,CAAAM,OAAA,CAAAoB,aAAA,CAACvB,gBAAA,CAAAG,OAAe;MAACyC,OAAO,EAAC;IAAQ,CAAE,CAAC,CAAC;IAE5C,IAAAC,cAAM,EAACC,aAAM,CAACC,SAAS,CAAC,QAAQ,EAAE;MAAEC,IAAI,EAAE;IAAS,CAAC,CAAC,CAAC,CAACC,iBAAiB,CAAC,CAAC;EAC9E,CAAC,CAAC;AACN,CAAC,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.copyableContentToClipboard = void 0;
|
|
7
|
+
var _format = require("@chayns-components/format");
|
|
8
|
+
const RICH_MIME_TYPES = ['text/plain', 'text/markdown', 'text/html'];
|
|
9
|
+
const createClipboardItem = data => new ClipboardItem(data);
|
|
10
|
+
const supportsRichMimeTypes = () => {
|
|
11
|
+
if (typeof ClipboardItem === 'undefined' || typeof ClipboardItem.supports !== 'function') {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
return RICH_MIME_TYPES.every(mimeType => ClipboardItem.supports(mimeType));
|
|
15
|
+
};
|
|
16
|
+
const copyPlainText = async source => {
|
|
17
|
+
if (typeof ClipboardItem !== 'undefined') {
|
|
18
|
+
try {
|
|
19
|
+
await navigator.clipboard.write([createClipboardItem({
|
|
20
|
+
'text/plain': new Blob([source], {
|
|
21
|
+
type: 'text/plain'
|
|
22
|
+
})
|
|
23
|
+
})]);
|
|
24
|
+
return;
|
|
25
|
+
} catch {
|
|
26
|
+
// Use writeText as the final browser-compatible fallback.
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
await navigator.clipboard.writeText(source);
|
|
30
|
+
};
|
|
31
|
+
const copyableContentToClipboard = async source => {
|
|
32
|
+
if (!navigator.clipboard) {
|
|
33
|
+
throw new Error('Clipboard API is not available.');
|
|
34
|
+
}
|
|
35
|
+
if (typeof ClipboardItem === 'undefined' || !supportsRichMimeTypes()) {
|
|
36
|
+
await copyPlainText(source);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const {
|
|
40
|
+
html
|
|
41
|
+
} = (0, _format.formatStringToHtml)(source);
|
|
42
|
+
try {
|
|
43
|
+
await navigator.clipboard.write([createClipboardItem({
|
|
44
|
+
'text/plain': new Blob([source], {
|
|
45
|
+
type: 'text/plain'
|
|
46
|
+
}),
|
|
47
|
+
'text/markdown': new Blob([source], {
|
|
48
|
+
type: 'text/markdown'
|
|
49
|
+
}),
|
|
50
|
+
'text/html': new Blob([html], {
|
|
51
|
+
type: 'text/html'
|
|
52
|
+
})
|
|
53
|
+
})]);
|
|
54
|
+
} catch {
|
|
55
|
+
await copyPlainText(source);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
exports.copyableContentToClipboard = copyableContentToClipboard;
|
|
59
|
+
//# sourceMappingURL=copyableContentClipboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copyableContentClipboard.js","names":["_format","require","RICH_MIME_TYPES","createClipboardItem","data","ClipboardItem","supportsRichMimeTypes","supports","every","mimeType","copyPlainText","source","navigator","clipboard","write","Blob","type","writeText","copyableContentToClipboard","Error","html","formatStringToHtml","exports"],"sources":["../../../../src/components/copyable-content/copyableContentClipboard.ts"],"sourcesContent":["import { formatStringToHtml } from '@chayns-components/format';\n\nconst RICH_MIME_TYPES = ['text/plain', 'text/markdown', 'text/html'] as const;\n\nconst createClipboardItem = (data: Record<string, Blob>) => new ClipboardItem(data);\n\nconst supportsRichMimeTypes = () => {\n if (typeof ClipboardItem === 'undefined' || typeof ClipboardItem.supports !== 'function') {\n return true;\n }\n\n return RICH_MIME_TYPES.every((mimeType) => ClipboardItem.supports(mimeType));\n};\n\nconst copyPlainText = async (source: string) => {\n if (typeof ClipboardItem !== 'undefined') {\n try {\n await navigator.clipboard.write([\n createClipboardItem({ 'text/plain': new Blob([source], { type: 'text/plain' }) }),\n ]);\n return;\n } catch {\n // Use writeText as the final browser-compatible fallback.\n }\n }\n\n await navigator.clipboard.writeText(source);\n};\n\nexport const copyableContentToClipboard = async (source: string) => {\n if (!navigator.clipboard) {\n throw new Error('Clipboard API is not available.');\n }\n\n if (typeof ClipboardItem === 'undefined' || !supportsRichMimeTypes()) {\n await copyPlainText(source);\n return;\n }\n\n const { html } = formatStringToHtml(source);\n\n try {\n await navigator.clipboard.write([\n createClipboardItem({\n 'text/plain': new Blob([source], { type: 'text/plain' }),\n 'text/markdown': new Blob([source], { type: 'text/markdown' }),\n 'text/html': new Blob([html], { type: 'text/html' }),\n }),\n ]);\n } catch {\n await copyPlainText(source);\n }\n};\n"],"mappings":";;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAEA,MAAMC,eAAe,GAAG,CAAC,YAAY,EAAE,eAAe,EAAE,WAAW,CAAU;AAE7E,MAAMC,mBAAmB,GAAIC,IAA0B,IAAK,IAAIC,aAAa,CAACD,IAAI,CAAC;AAEnF,MAAME,qBAAqB,GAAGA,CAAA,KAAM;EAChC,IAAI,OAAOD,aAAa,KAAK,WAAW,IAAI,OAAOA,aAAa,CAACE,QAAQ,KAAK,UAAU,EAAE;IACtF,OAAO,IAAI;EACf;EAEA,OAAOL,eAAe,CAACM,KAAK,CAAEC,QAAQ,IAAKJ,aAAa,CAACE,QAAQ,CAACE,QAAQ,CAAC,CAAC;AAChF,CAAC;AAED,MAAMC,aAAa,GAAG,MAAOC,MAAc,IAAK;EAC5C,IAAI,OAAON,aAAa,KAAK,WAAW,EAAE;IACtC,IAAI;MACA,MAAMO,SAAS,CAACC,SAAS,CAACC,KAAK,CAAC,CAC5BX,mBAAmB,CAAC;QAAE,YAAY,EAAE,IAAIY,IAAI,CAAC,CAACJ,MAAM,CAAC,EAAE;UAAEK,IAAI,EAAE;QAAa,CAAC;MAAE,CAAC,CAAC,CACpF,CAAC;MACF;IACJ,CAAC,CAAC,MAAM;MACJ;IAAA;EAER;EAEA,MAAMJ,SAAS,CAACC,SAAS,CAACI,SAAS,CAACN,MAAM,CAAC;AAC/C,CAAC;AAEM,MAAMO,0BAA0B,GAAG,MAAOP,MAAc,IAAK;EAChE,IAAI,CAACC,SAAS,CAACC,SAAS,EAAE;IACtB,MAAM,IAAIM,KAAK,CAAC,iCAAiC,CAAC;EACtD;EAEA,IAAI,OAAOd,aAAa,KAAK,WAAW,IAAI,CAACC,qBAAqB,CAAC,CAAC,EAAE;IAClE,MAAMI,aAAa,CAACC,MAAM,CAAC;IAC3B;EACJ;EAEA,MAAM;IAAES;EAAK,CAAC,GAAG,IAAAC,0BAAkB,EAACV,MAAM,CAAC;EAE3C,IAAI;IACA,MAAMC,SAAS,CAACC,SAAS,CAACC,KAAK,CAAC,CAC5BX,mBAAmB,CAAC;MAChB,YAAY,EAAE,IAAIY,IAAI,CAAC,CAACJ,MAAM,CAAC,EAAE;QAAEK,IAAI,EAAE;MAAa,CAAC,CAAC;MACxD,eAAe,EAAE,IAAID,IAAI,CAAC,CAACJ,MAAM,CAAC,EAAE;QAAEK,IAAI,EAAE;MAAgB,CAAC,CAAC;MAC9D,WAAW,EAAE,IAAID,IAAI,CAAC,CAACK,IAAI,CAAC,EAAE;QAAEJ,IAAI,EAAE;MAAY,CAAC;IACvD,CAAC,CAAC,CACL,CAAC;EACN,CAAC,CAAC,MAAM;IACJ,MAAMN,aAAa,CAACC,MAAM,CAAC;EAC/B;AACJ,CAAC;AAACW,OAAA,CAAAJ,0BAAA,GAAAA,0BAAA","ignoreList":[]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _format = require("@chayns-components/format");
|
|
4
|
+
var _vitest = require("vitest");
|
|
5
|
+
var _copyableContentClipboard = require("./copyableContentClipboard");
|
|
6
|
+
const source = '# Heading\n\n[Link](https://example.com)';
|
|
7
|
+
const readBlob = blob => new Promise((resolve, reject) => {
|
|
8
|
+
const reader = new FileReader();
|
|
9
|
+
reader.onerror = () => reject(reader.error);
|
|
10
|
+
reader.onload = () => resolve(reader.result);
|
|
11
|
+
reader.readAsText(blob);
|
|
12
|
+
});
|
|
13
|
+
(0, _vitest.describe)('copyableContentToClipboard', () => {
|
|
14
|
+
(0, _vitest.afterEach)(() => {
|
|
15
|
+
_vitest.vi.restoreAllMocks();
|
|
16
|
+
});
|
|
17
|
+
(0, _vitest.it)('writes exact source as plain text and markdown with safe HTML', async () => {
|
|
18
|
+
const write = _vitest.vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();
|
|
19
|
+
await (0, _copyableContentClipboard.copyableContentToClipboard)(source);
|
|
20
|
+
const item = write.mock.calls[0][0][0];
|
|
21
|
+
await (0, _vitest.expect)(readBlob(await item.getType('text/plain'))).resolves.toBe(source);
|
|
22
|
+
await (0, _vitest.expect)(readBlob(await item.getType('text/markdown'))).resolves.toBe(source);
|
|
23
|
+
await (0, _vitest.expect)(readBlob(await item.getType('text/html'))).resolves.toBe((0, _format.formatStringToHtml)(source).html);
|
|
24
|
+
});
|
|
25
|
+
(0, _vitest.it)('uses a plain ClipboardItem when rich MIME types are unsupported', async () => {
|
|
26
|
+
_vitest.vi.stubGlobal('ClipboardItem', Object.assign(class {}, {
|
|
27
|
+
supports: () => false
|
|
28
|
+
}));
|
|
29
|
+
const write = _vitest.vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();
|
|
30
|
+
await (0, _copyableContentClipboard.copyableContentToClipboard)(source);
|
|
31
|
+
(0, _vitest.expect)(write).toHaveBeenCalledTimes(1);
|
|
32
|
+
});
|
|
33
|
+
(0, _vitest.it)('uses writeText after ClipboardItem writes fail', async () => {
|
|
34
|
+
_vitest.vi.spyOn(navigator.clipboard, 'write').mockRejectedValue(new Error('denied'));
|
|
35
|
+
const writeText = _vitest.vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();
|
|
36
|
+
await (0, _copyableContentClipboard.copyableContentToClipboard)(source);
|
|
37
|
+
(0, _vitest.expect)(writeText).toHaveBeenCalledWith(source);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=copyableContentClipboard.test.js.map
|
|
@@ -0,0 +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","it","write","spyOn","navigator","clipboard","mockResolvedValue","copyableContentToClipboard","item","mock","calls","expect","getType","resolves","toBe","formatStringToHtml","html","stubGlobal","Object","assign","supports","toHaveBeenCalledTimes","mockRejectedValue","Error","writeText","toHaveBeenCalledWith"],"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 });\n\n it('writes exact source as plain text and markdown with safe HTML', 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 await expect(readBlob(await item.getType('text/plain'))).resolves.toBe(source);\n await expect(readBlob(await item.getType('text/markdown'))).resolves.toBe(source);\n await expect(readBlob(await item.getType('text/html'))).resolves.toBe(\n formatStringToHtml(source).html,\n );\n });\n\n it('uses a plain ClipboardItem when rich MIME types are unsupported', async () => {\n vi.stubGlobal('ClipboardItem', Object.assign(class {}, { supports: () => false }));\n const write = vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n expect(write).toHaveBeenCalledTimes(1);\n });\n\n it('uses writeText after 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(source);\n });\n});\n"],"mappings":"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","ignoreList":[]}
|