Skip to content

Commit 62fa553

Browse files
authored
feat: header anchor button (#698)
1 parent 29a987b commit 62fa553

File tree

21 files changed

+370
-87
lines changed

21 files changed

+370
-87
lines changed

e2e/fixtures/utils/storyMeta.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,25 @@ import '@diplodoc/transform/dist/css/yfm.min.css';
33
import '@diplodoc/transform/dist/css/print.css';
44
import '@diplodoc/transform/dist/js/yfm.js';
55

6+
import {OptionsType} from '../../../lib/typings';
7+
68
import {transformMd} from './transformPreset';
79

810
export type MarkdownSnippetProps = {
911
snippet: string;
1012
additionalRootClassnames?: string[];
13+
extraOptions?: OptionsType;
1114
};
1215

1316
export type MarkdownSnippetStory = StoryObj<MarkdownSnippetProps>;
1417

1518
export const getSnippetMeta = (): Meta<MarkdownSnippetProps> => ({
16-
render: ({snippet, additionalRootClassnames = []}) => {
19+
render: ({snippet, additionalRootClassnames = [], extraOptions}) => {
1720
const div = document.createElement('div');
1821

1922
div.classList.add('yfm', ...additionalRootClassnames);
2023
div.id = 'yfm-root';
21-
div.innerHTML = transformMd(snippet);
24+
div.innerHTML = transformMd(snippet, extraOptions);
2225

2326
return div;
2427
},

e2e/fixtures/utils/transformPreset.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ import table from '@diplodoc/transform/lib/plugins/table';
1313
import notes from '@diplodoc/transform/lib/plugins/notes';
1414
import transform from '@diplodoc/transform';
1515

16-
const transformOptions = {
16+
import {OptionsType} from '../../../lib/typings';
17+
18+
const transformOptions: OptionsType = {
1719
lang: 'en',
1820
path: '',
1921
plugins: [
@@ -33,8 +35,8 @@ const transformOptions = {
3335
],
3436
};
3537

36-
export const transformMd = (input: string): string => {
37-
const {result} = transform(input, transformOptions);
38+
export const transformMd = (input: string, customOptions?: OptionsType): string => {
39+
const {result} = transform(input, {...transformOptions, ...customOptions});
3840

3941
return result.html;
4042
};

e2e/playwright/playwright.config.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,13 @@ export default defineConfig({
3131
projects: [
3232
{
3333
name: 'chromium',
34-
use: {...devices['Desktop Chrome'], viewport: narrowerViewport},
34+
use: {
35+
...devices['Desktop Chrome'],
36+
viewport: narrowerViewport,
37+
contextOptions: {
38+
permissions: ['clipboard-read', 'clipboard-write'],
39+
},
40+
},
3541
},
3642

3743
{
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import dedent from 'ts-dedent';
2+
3+
import {MarkdownSnippetStory, getSnippetMeta} from '../../fixtures/utils/storyMeta';
4+
5+
export default {...getSnippetMeta(), title: 'Builtins/Anchor/ClipboardAnchor'};
6+
7+
export const Base: MarkdownSnippetStory = {
8+
name: 'Base Clipboard Anchor',
9+
args: {
10+
snippet: dedent`
11+
## Title
12+
`,
13+
extraOptions: {
14+
useCommonAnchorButtons: true,
15+
},
16+
},
17+
};
18+
19+
export const Custom: MarkdownSnippetStory = {
20+
name: 'Custom Clipboard Anchor',
21+
args: {
22+
snippet: dedent`
23+
## Title {#test-id}
24+
`,
25+
extraOptions: {
26+
useCommonAnchorButtons: true,
27+
},
28+
},
29+
};

e2e/tests/anchor.spec.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import {describeStory} from 'fixtures/utils/testStoryModule';
2+
3+
import * as stories from '../stories/anchor/ClipboardAnchor.stories';
4+
import {expect, test} from '../fixtures/test';
5+
6+
test.beforeEach(({browserName}) =>
7+
test.skip(browserName !== 'chromium', `"${browserName}" does not support clipboard`),
8+
);
9+
10+
describeStory(stories, 'Base', () => {
11+
test('Clipboard anchor click should return correct page url', async ({yfmRoot, page}) => {
12+
const anchorButton = yfmRoot.getByRole('button');
13+
await anchorButton.click();
14+
15+
const handle = await page.evaluateHandle(() => navigator.clipboard.readText());
16+
const clipboardContent = await handle.jsonValue();
17+
18+
expect(clipboardContent).toEqual(page.url() + '#title');
19+
});
20+
});
21+
22+
describeStory(stories, 'Custom', () => {
23+
test('Clipboard anchor click should return custom anchor url', async ({yfmRoot, page}) => {
24+
const anchorButton = yfmRoot.getByRole('button');
25+
await anchorButton.click();
26+
27+
const handle = await page.evaluateHandle(() => navigator.clipboard.readText());
28+
const clipboardContent = await handle.jsonValue();
29+
30+
expect(clipboardContent).toEqual(page.url() + '#test-id');
31+
});
32+
});

playground/package-lock.json

Lines changed: 32 additions & 30 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

playground/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
},
7373
"dependencies": {
7474
"@diplodoc/markdown-it-markdown-renderer": "^0.14.4",
75-
"@doc-tools/yfm-editor": "^12.2.1",
75+
"@doc-tools/yfm-editor": "^12.4.0",
7676
"@gravity-ui/page-constructor": "^5.12.0",
7777
"@monaco-editor/react": "^4.5.2",
7878
"js-yaml": "^4.1.0",

src/js/anchor.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import {copyToClipboard, getEventTarget, isCustom} from './utils';
2+
3+
const ANCHOR_BUTTON_SELECTOR = '.yfm-clipboard-anchor';
4+
5+
if (typeof document !== 'undefined') {
6+
document.addEventListener('click', (event) => {
7+
const target = getEventTarget(event) as HTMLElement;
8+
9+
if (isCustom(event) || !target.matches(ANCHOR_BUTTON_SELECTOR)) {
10+
return;
11+
}
12+
13+
const href = target.getAttribute('data-href') || '';
14+
const link = new URL(href, window.location.href).toString();
15+
16+
copyToClipboard(link);
17+
});
18+
}

src/js/base.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
import './polyfill';
22
import './code';
3+
import './anchor';

src/js/code.ts

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,7 @@
1-
import {getEventTarget, isCustom} from './utils';
1+
import {copyToClipboard, getEventTarget, isCustom} from './utils';
22

33
const BUTTON_SELECTOR = '.yfm-clipboard-button';
44

5-
function copyToClipboard(text: string) {
6-
if (!text) {
7-
return Promise.resolve();
8-
}
9-
10-
if (navigator.clipboard && typeof navigator.clipboard.writeText) {
11-
return navigator.clipboard.writeText(text);
12-
}
13-
const textarea = document.createElement('textarea');
14-
textarea.setAttribute('style', 'position: absolute; left: 1000%');
15-
textarea.textContent = text;
16-
document.body.append(textarea);
17-
18-
textarea.select();
19-
document.execCommand('copy');
20-
21-
document.body.removeChild(textarea);
22-
23-
return Promise.resolve();
24-
}
25-
265
function notifySuccess(svgButton: HTMLElement | null) {
276
if (!svgButton) {
287
return;

0 commit comments

Comments
 (0)