@zipify/wysiwyg 1.0.0-dev.42 → 1.0.0-dev.45
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/config/vite/example.config.js +25 -0
- package/dist/wysiwyg.css +1 -1
- package/dist/wysiwyg.mjs +3143 -3046
- package/example/ExampleApp.vue +5 -0
- package/example/{example.html → index.html} +1 -0
- package/lib/Wysiwyg.vue +4 -5
- package/lib/__tests__/utils/NodeFactory.js +13 -0
- package/lib/components/base/__tests__/Modal.test.js +2 -3
- package/lib/components/base/composables/__tests__/useValidator.test.js +44 -0
- package/lib/components/base/composables/useValidator.js +7 -3
- package/lib/components/toolbar/__tests__/Toolbar.test.js +2 -3
- package/lib/components/toolbar/controls/StylePresetControl.vue +14 -1
- package/lib/components/toolbar/controls/__tests__/StylePresetControl.test.js +16 -0
- package/lib/components/toolbar/controls/link/LinkControl.vue +28 -25
- package/lib/components/toolbar/controls/link/__tests__/LinkControl.test.js +79 -0
- package/lib/components/toolbar/controls/link/__tests__/LinkControlHeader.test.js +42 -0
- package/lib/components/toolbar/controls/link/composables/__tests__/__snapshots__/useLink.test.js.snap +8 -0
- package/lib/components/toolbar/controls/link/composables/__tests__/useLink.test.js +114 -0
- package/lib/components/toolbar/controls/link/destination/LinkControlDestination.vue +2 -2
- package/lib/components/toolbar/controls/link/destination/LinkControlUrl.vue +2 -2
- package/lib/components/toolbar/controls/link/destination/__tests__/LinkControlPageBlock.test.js +36 -0
- package/lib/components/toolbar/controls/link/destination/__tests__/LinkControlUrl.test.js +46 -0
- package/lib/components/toolbar/controls/link/destination/__tests__/__snapshots__/LinkControlPageBlock.test.js.snap +9 -0
- package/lib/components/toolbar/controls/link/destination/__tests__/__snapshots__/LinkControlUrl.test.js.snap +17 -0
- package/lib/composables/useToolbar.js +11 -0
- package/lib/directives/__tests__/outClick.test.js +6 -0
- package/lib/directives/outClick.js +12 -15
- package/lib/enums/Alignments.js +10 -1
- package/lib/extensions/Alignment.js +7 -5
- package/lib/extensions/FontSize.js +8 -2
- package/lib/extensions/FontWeight.js +3 -1
- package/lib/extensions/LineHeight.js +30 -36
- package/lib/extensions/Link.js +3 -15
- package/lib/extensions/TextDecoration.js +18 -0
- package/lib/extensions/__tests__/FontSize.test.js +2 -1
- package/lib/extensions/__tests__/FontWeight.test.js +8 -0
- package/lib/extensions/__tests__/LineHeight.test.js +12 -6
- package/lib/extensions/__tests__/Link.test.js +102 -0
- package/lib/extensions/__tests__/TextDecoration.test.js +24 -0
- package/lib/extensions/__tests__/__snapshots__/FontWeight.test.js.snap +17 -0
- package/lib/extensions/__tests__/__snapshots__/Link.test.js.snap +225 -0
- package/lib/extensions/__tests__/__snapshots__/TextDecoration.test.js.snap +90 -0
- package/lib/extensions/core/plugins/PastePlugin.js +23 -14
- package/lib/extensions/index.js +10 -5
- package/lib/services/ContentNormalizer.js +55 -15
- package/lib/services/__tests__/ContentNormalizer.test.js +39 -4
- package/lib/utils/__tests__/convertAlignment.test.js +16 -0
- package/lib/utils/__tests__/convertFontSize.test.js +21 -0
- package/lib/utils/__tests__/convertLineHeight.test.js +21 -0
- package/lib/utils/convertAlignment.js +12 -0
- package/lib/utils/convertFontSize.js +8 -0
- package/lib/utils/convertLineHeight.js +17 -0
- package/lib/utils/index.js +3 -0
- package/package.json +3 -13
- package/config/webpack/example.config.js +0 -88
- package/config/webpack/lib.config.js +0 -43
- package/config/webpack/loaders/index.js +0 -6
- package/config/webpack/loaders/js-loader.js +0 -5
- package/config/webpack/loaders/style-loader.js +0 -9
- package/config/webpack/loaders/svg-loader.js +0 -4
- package/config/webpack/loaders/vue-loader.js +0 -4
- package/config/webpack/settings.js +0 -9
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ref, h } from 'vue';
|
|
2
|
+
import { shallowMount } from '@vue/test-utils';
|
|
3
|
+
import { Checkbox, TextField } from '../../../../../base';
|
|
4
|
+
import LinkControlUrl from '../LinkControlUrl';
|
|
5
|
+
|
|
6
|
+
function createComponent() {
|
|
7
|
+
return shallowMount(LinkControlUrl, {
|
|
8
|
+
propsData: {
|
|
9
|
+
validator: { error: ref({ value: null }) },
|
|
10
|
+
href: '',
|
|
11
|
+
isTargetBlank: false
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
stubs: {
|
|
15
|
+
TextField: {
|
|
16
|
+
render: () => h('div'),
|
|
17
|
+
props: ['value']
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
Checkbox: {
|
|
21
|
+
render: () => h('div'),
|
|
22
|
+
props: ['value']
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe('update link', () => {
|
|
29
|
+
test('should update link url', () => {
|
|
30
|
+
const wrapper = createComponent();
|
|
31
|
+
const textFieldWrapper = wrapper.findComponent(TextField);
|
|
32
|
+
|
|
33
|
+
textFieldWrapper.vm.$emit('input', 'Some text');
|
|
34
|
+
|
|
35
|
+
expect(wrapper.emitted('update-link')).toMatchSnapshot();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('should update link target', () => {
|
|
39
|
+
const wrapper = createComponent();
|
|
40
|
+
const textFieldWrapper = wrapper.findComponent(Checkbox);
|
|
41
|
+
|
|
42
|
+
textFieldWrapper.vm.$emit('input', true);
|
|
43
|
+
|
|
44
|
+
expect(wrapper.emitted('update-target')).toMatchSnapshot();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`update link should update link target 1`] = `
|
|
4
|
+
Array [
|
|
5
|
+
Array [
|
|
6
|
+
true,
|
|
7
|
+
],
|
|
8
|
+
]
|
|
9
|
+
`;
|
|
10
|
+
|
|
11
|
+
exports[`update link should update link url 1`] = `
|
|
12
|
+
Array [
|
|
13
|
+
Array [
|
|
14
|
+
"Some text",
|
|
15
|
+
],
|
|
16
|
+
]
|
|
17
|
+
`;
|
|
@@ -12,6 +12,17 @@ export function useToolbar({ wrapperRef, offsets, isActiveRef }) {
|
|
|
12
12
|
{
|
|
13
13
|
name: 'offset',
|
|
14
14
|
options: { offset: offsets }
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: 'preventOverflow',
|
|
18
|
+
options: {
|
|
19
|
+
altAxis: true,
|
|
20
|
+
padding: 2
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'flip',
|
|
25
|
+
enabled: false
|
|
15
26
|
}
|
|
16
27
|
]
|
|
17
28
|
});
|
|
@@ -10,6 +10,7 @@ describe('out click', () => {
|
|
|
10
10
|
await waitAsyncOperation();
|
|
11
11
|
|
|
12
12
|
document.dispatchEvent(new Event('click'));
|
|
13
|
+
await waitAsyncOperation();
|
|
13
14
|
|
|
14
15
|
expect(onOutClick).toHaveBeenCalled();
|
|
15
16
|
});
|
|
@@ -22,6 +23,7 @@ describe('out click', () => {
|
|
|
22
23
|
await waitAsyncOperation();
|
|
23
24
|
|
|
24
25
|
el.dispatchEvent(new Event('click'));
|
|
26
|
+
await waitAsyncOperation();
|
|
25
27
|
|
|
26
28
|
expect(onOutClick).not.toHaveBeenCalled();
|
|
27
29
|
});
|
|
@@ -36,6 +38,7 @@ describe('out click', () => {
|
|
|
36
38
|
await waitAsyncOperation();
|
|
37
39
|
|
|
38
40
|
child.dispatchEvent(new Event('click'));
|
|
41
|
+
await waitAsyncOperation();
|
|
39
42
|
|
|
40
43
|
expect(onOutClick).not.toHaveBeenCalled();
|
|
41
44
|
});
|
|
@@ -50,6 +53,7 @@ describe('out click', () => {
|
|
|
50
53
|
outClick.unbind(el);
|
|
51
54
|
|
|
52
55
|
document.dispatchEvent(new Event('click'));
|
|
56
|
+
await waitAsyncOperation();
|
|
53
57
|
|
|
54
58
|
expect(onOutClick).not.toHaveBeenCalled();
|
|
55
59
|
});
|
|
@@ -65,6 +69,7 @@ describe('out click', () => {
|
|
|
65
69
|
await waitAsyncOperation();
|
|
66
70
|
|
|
67
71
|
document.dispatchEvent(new Event('click'));
|
|
72
|
+
await waitAsyncOperation();
|
|
68
73
|
|
|
69
74
|
expect(onOutClick).toHaveBeenCalled();
|
|
70
75
|
});
|
|
@@ -80,6 +85,7 @@ describe('out click', () => {
|
|
|
80
85
|
await waitAsyncOperation();
|
|
81
86
|
|
|
82
87
|
document.dispatchEvent(new Event('click'));
|
|
88
|
+
await waitAsyncOperation();
|
|
83
89
|
|
|
84
90
|
expect(onOutClick).not.toHaveBeenCalled();
|
|
85
91
|
});
|
|
@@ -2,18 +2,15 @@ import { ContextWindow } from '../services';
|
|
|
2
2
|
|
|
3
3
|
const dataStorage = new WeakMap();
|
|
4
4
|
|
|
5
|
-
function
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
ContextWindow.document.addEventListener('click', onClick);
|
|
10
|
-
}
|
|
5
|
+
function toggleListener(toEnable, onClick) {
|
|
6
|
+
const args = ['click', onClick, { capture: true }];
|
|
7
|
+
const action = toEnable ? 'addEventListener' : 'removeEventListener';
|
|
11
8
|
|
|
12
|
-
function removeListener(onClick) {
|
|
13
9
|
if (ContextWindow.window !== window) {
|
|
14
|
-
window.document
|
|
10
|
+
window.document[action](...args);
|
|
15
11
|
}
|
|
16
|
-
|
|
12
|
+
|
|
13
|
+
ContextWindow.document[action](...args);
|
|
17
14
|
}
|
|
18
15
|
|
|
19
16
|
export const outClick = {
|
|
@@ -24,13 +21,15 @@ export const outClick = {
|
|
|
24
21
|
function onClick(event) {
|
|
25
22
|
const isInside = event.target === el || el.contains(event.target);
|
|
26
23
|
|
|
27
|
-
if (event.target.isConnected && !isInside)
|
|
24
|
+
if (event.target.isConnected && !isInside) {
|
|
25
|
+
setTimeout(() => onOutClick(event));
|
|
26
|
+
}
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
dataStorage.set(el, { callback: onClick, isEnabled });
|
|
31
30
|
|
|
32
31
|
if (isEnabled) {
|
|
33
|
-
setTimeout(() =>
|
|
32
|
+
setTimeout(() => toggleListener(true, onClick));
|
|
34
33
|
}
|
|
35
34
|
},
|
|
36
35
|
|
|
@@ -40,14 +39,12 @@ export const outClick = {
|
|
|
40
39
|
|
|
41
40
|
if (isEnabled === data.isEnabled) return;
|
|
42
41
|
|
|
43
|
-
isEnabled
|
|
44
|
-
? attachListener(data.callback)
|
|
45
|
-
: removeListener(data.callback);
|
|
42
|
+
toggleListener(isEnabled, data.callback);
|
|
46
43
|
|
|
47
44
|
dataStorage.set(el, { callback: data.callback, isEnabled });
|
|
48
45
|
},
|
|
49
46
|
|
|
50
47
|
unbind(el) {
|
|
51
|
-
|
|
48
|
+
toggleListener(false, dataStorage.get(el).callback);
|
|
52
49
|
}
|
|
53
50
|
};
|
package/lib/enums/Alignments.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Extension } from '@tiptap/vue-2';
|
|
2
2
|
import { computed } from 'vue';
|
|
3
|
-
import { createCommand, createKeyboardShortcut, renderInlineSetting } from '../utils';
|
|
3
|
+
import { convertAlignment, createCommand, createKeyboardShortcut, renderInlineSetting } from '../utils';
|
|
4
4
|
import { Alignments, NodeTypes, TextSettings } from '../enums';
|
|
5
5
|
|
|
6
6
|
const DEFAULTS = {
|
|
@@ -20,11 +20,13 @@ export const Alignment = Extension.create({
|
|
|
20
20
|
isRequired: false,
|
|
21
21
|
|
|
22
22
|
parseHTML({ style }) {
|
|
23
|
-
|
|
23
|
+
const textAlign = convertAlignment(style.textAlign);
|
|
24
|
+
|
|
25
|
+
if (textAlign) {
|
|
24
26
|
return {
|
|
25
|
-
desktop:
|
|
26
|
-
tablet:
|
|
27
|
-
mobile:
|
|
27
|
+
desktop: textAlign,
|
|
28
|
+
tablet: textAlign,
|
|
29
|
+
mobile: textAlign
|
|
28
30
|
};
|
|
29
31
|
}
|
|
30
32
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Mark } from '@tiptap/vue-2';
|
|
2
2
|
import { computed } from 'vue';
|
|
3
|
-
import { createCommand, createKeyboardShortcut, renderMark } from '../utils';
|
|
3
|
+
import { convertFontSize, createCommand, createKeyboardShortcut, renderMark } from '../utils';
|
|
4
4
|
import { TextSettings } from '../enums';
|
|
5
5
|
|
|
6
6
|
export const FontSize = Mark.create({
|
|
@@ -63,7 +63,13 @@ export const FontSize = Mark.create({
|
|
|
63
63
|
}),
|
|
64
64
|
|
|
65
65
|
parseHTML() {
|
|
66
|
-
const parseSize = (value) =>
|
|
66
|
+
const parseSize = (value) => {
|
|
67
|
+
if (!value) return null;
|
|
68
|
+
|
|
69
|
+
const converted = convertFontSize(value, this.options.wrapperRef.value);
|
|
70
|
+
|
|
71
|
+
return String(converted);
|
|
72
|
+
};
|
|
67
73
|
|
|
68
74
|
return [
|
|
69
75
|
{
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Extension } from '@tiptap/vue-2';
|
|
2
2
|
import { computed } from 'vue';
|
|
3
|
-
import { createCommand, renderInlineSetting } from '../utils';
|
|
3
|
+
import { createCommand, renderInlineSetting, convertLineHeight } from '../utils';
|
|
4
4
|
import { NodeTypes, TextSettings } from '../enums';
|
|
5
|
-
import { ContextWindow } from '../services';
|
|
6
5
|
|
|
7
6
|
const DEFAULTS = {
|
|
8
7
|
mobile: null,
|
|
@@ -13,51 +12,46 @@ const DEFAULTS = {
|
|
|
13
12
|
export const LineHeight = Extension.create({
|
|
14
13
|
name: TextSettings.LINE_HEIGHT,
|
|
15
14
|
|
|
16
|
-
addGlobalAttributes
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
addGlobalAttributes() {
|
|
16
|
+
return [
|
|
17
|
+
{
|
|
18
|
+
types: [NodeTypes.PARAGRAPH, NodeTypes.HEADING],
|
|
19
|
+
attributes: {
|
|
20
|
+
[TextSettings.LINE_HEIGHT]: {
|
|
21
|
+
isRequired: false,
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
parseHTML: (element) => {
|
|
24
|
+
if (element.matches('[style*="--zw-line-height"]')) {
|
|
25
|
+
const mobile = element.style.getPropertyValue('--zw-line-height-mobile') || null;
|
|
26
|
+
const tablet = element.style.getPropertyValue('--zw-line-height-tablet') || null;
|
|
27
|
+
const desktop = element.style.getPropertyValue('--zw-line-height-desktop') || null;
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const value = element.style.lineHeight;
|
|
29
|
+
return { mobile, tablet, desktop };
|
|
30
|
+
}
|
|
33
31
|
|
|
34
|
-
|
|
32
|
+
const value = element.style.lineHeight;
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
return { desktop: value, tablet: value, mobile: value };
|
|
38
|
-
}
|
|
34
|
+
if (!value) return null;
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
const childFontSize = element.firstElementChild.style.fontSize;
|
|
42
|
-
const fontSize = childFontSize || ContextWindow.getComputedStyle(ContextWindow.body).fontSize;
|
|
43
|
-
const relative = (parseFloat(value) / parseFloat(fontSize)).toFixed(2);
|
|
36
|
+
const converted = convertLineHeight(value, element, this.options.wrapperRef.value);
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
return { desktop: converted, tablet: converted, mobile: converted };
|
|
39
|
+
},
|
|
47
40
|
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
renderHTML(attrs) {
|
|
42
|
+
if (!attrs.line_height) return null;
|
|
50
43
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
44
|
+
return renderInlineSetting({
|
|
45
|
+
line_height_mobile: attrs.line_height.mobile,
|
|
46
|
+
line_height_tablet: attrs.line_height.tablet,
|
|
47
|
+
line_height_desktop: attrs.line_height.desktop
|
|
48
|
+
});
|
|
49
|
+
}
|
|
56
50
|
}
|
|
57
51
|
}
|
|
58
52
|
}
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
];
|
|
54
|
+
},
|
|
61
55
|
|
|
62
56
|
addCommands() {
|
|
63
57
|
return {
|
package/lib/extensions/Link.js
CHANGED
|
@@ -8,13 +8,7 @@ export const Link = Base.extend({
|
|
|
8
8
|
addOptions() {
|
|
9
9
|
return {
|
|
10
10
|
...this.parent?.(),
|
|
11
|
-
openOnClick: false
|
|
12
|
-
HTMLAttributes: {
|
|
13
|
-
target: LinkTargets.SELF
|
|
14
|
-
},
|
|
15
|
-
preset: {},
|
|
16
|
-
basePresetClass: null,
|
|
17
|
-
pageBlocks: []
|
|
11
|
+
openOnClick: false
|
|
18
12
|
};
|
|
19
13
|
},
|
|
20
14
|
|
|
@@ -32,14 +26,8 @@ export const Link = Base.extend({
|
|
|
32
26
|
},
|
|
33
27
|
|
|
34
28
|
target: {
|
|
35
|
-
default:
|
|
36
|
-
parseHTML: (element) =>
|
|
37
|
-
const target = element.getAttribute('target');
|
|
38
|
-
|
|
39
|
-
if (!target) return LinkTargets.SELF;
|
|
40
|
-
|
|
41
|
-
return target;
|
|
42
|
-
}
|
|
29
|
+
default: LinkTargets.SELF,
|
|
30
|
+
parseHTML: (element) => element.getAttribute('target') || LinkTargets.SELF
|
|
43
31
|
},
|
|
44
32
|
|
|
45
33
|
destination: {
|
|
@@ -108,6 +108,24 @@ export const TextDecoration = Mark.create({
|
|
|
108
108
|
{
|
|
109
109
|
style: 'text-decoration-line',
|
|
110
110
|
getAttrs
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
style: 'text-decoration',
|
|
114
|
+
getAttrs
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
tag: 's',
|
|
118
|
+
attrs: {
|
|
119
|
+
underline: false,
|
|
120
|
+
strike_through: true
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
tag: 'u',
|
|
125
|
+
attrs: {
|
|
126
|
+
underline: true,
|
|
127
|
+
strike_through: false
|
|
128
|
+
}
|
|
111
129
|
}
|
|
112
130
|
];
|
|
113
131
|
},
|
|
@@ -211,4 +211,12 @@ describe('parsing html', () => {
|
|
|
211
211
|
|
|
212
212
|
expect(editor.getJSON()).toMatchSnapshot();
|
|
213
213
|
});
|
|
214
|
+
|
|
215
|
+
test('should ignore invalid value', () => {
|
|
216
|
+
const editor = createEditor({
|
|
217
|
+
content: '<p><span style="font-weight: var(--font-weight)">lorem</span> ipsum</p>'
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
expect(editor.getJSON()).toMatchSnapshot();
|
|
221
|
+
});
|
|
214
222
|
});
|
|
@@ -21,13 +21,17 @@ const MockStylePreset = Extension.create({
|
|
|
21
21
|
}
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
function createEditor({ content }) {
|
|
24
|
+
function createEditor({ content, wrapperEl }) {
|
|
25
25
|
return new Editor({
|
|
26
26
|
content: ContentNormalizer.normalize(content),
|
|
27
27
|
extensions: buildCoreExtensions().concat(
|
|
28
28
|
MockStylePreset,
|
|
29
|
-
DeviceManager.configure({
|
|
30
|
-
|
|
29
|
+
DeviceManager.configure({
|
|
30
|
+
device: ref('desktop')
|
|
31
|
+
}),
|
|
32
|
+
LineHeight.configure({
|
|
33
|
+
wrapperRef: ref(wrapperEl ?? document.createElement('div'))
|
|
34
|
+
})
|
|
31
35
|
)
|
|
32
36
|
});
|
|
33
37
|
}
|
|
@@ -124,14 +128,16 @@ describe('parsing html', () => {
|
|
|
124
128
|
});
|
|
125
129
|
|
|
126
130
|
test('should get value from text in px units with no font size', () => {
|
|
127
|
-
|
|
131
|
+
const wrapperEl = document.createElement('div');
|
|
132
|
+
|
|
133
|
+
wrapperEl.style.fontSize = '20px';
|
|
128
134
|
|
|
129
135
|
const editor = createEditor({
|
|
130
|
-
content: '<p style="line-height: 24px">test</p>'
|
|
136
|
+
content: '<p style="line-height: 24px">test</p>',
|
|
137
|
+
wrapperEl
|
|
131
138
|
});
|
|
132
139
|
|
|
133
140
|
expect(editor.getJSON()).toMatchSnapshot();
|
|
134
|
-
global.document.body.style.removeProperty('font-size');
|
|
135
141
|
});
|
|
136
142
|
|
|
137
143
|
test('should get value from rendered view', () => {
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { ref } from 'vue';
|
|
2
|
+
import { Editor } from '@tiptap/vue-2';
|
|
3
|
+
import { ContentNormalizer } from '../../services';
|
|
4
|
+
import { Link } from '../Link';
|
|
5
|
+
import { NodeFactory } from '../../__tests__/utils';
|
|
6
|
+
import { buildCoreExtensions } from '../core';
|
|
7
|
+
|
|
8
|
+
function createEditor({ content }) {
|
|
9
|
+
return new Editor({
|
|
10
|
+
content: ContentNormalizer.normalize(content),
|
|
11
|
+
extensions: buildCoreExtensions().concat(
|
|
12
|
+
Link.configure({
|
|
13
|
+
preset: 'link',
|
|
14
|
+
baseClass: 'zw ts-',
|
|
15
|
+
pageBlocks: ref([{ id: 987654 }])
|
|
16
|
+
})
|
|
17
|
+
)
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const createContent = (text) => NodeFactory.doc([
|
|
22
|
+
NodeFactory.paragraph([text])
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
const linkAttributes = (href) => ({
|
|
26
|
+
href: href ?? '/test',
|
|
27
|
+
text: 'Hello world link',
|
|
28
|
+
target: '_self',
|
|
29
|
+
destination: 'url'
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('apply link', () => {
|
|
33
|
+
test('should apply link', () => {
|
|
34
|
+
const editor = createEditor({
|
|
35
|
+
content: createContent(NodeFactory.text('hello world'))
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
editor.chain().selectAll().applyLink(linkAttributes()).run();
|
|
39
|
+
|
|
40
|
+
expect(editor.getJSON()).toMatchSnapshot();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('should apply link when no selected text', () => {
|
|
44
|
+
const editor = createEditor({
|
|
45
|
+
content: createContent(NodeFactory.text('Some text'))
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
editor.chain().setTextSelection(6).applyLink(linkAttributes()).run();
|
|
49
|
+
|
|
50
|
+
expect(editor.getJSON()).toMatchSnapshot();
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('parse html', () => {
|
|
55
|
+
test('should parse url links from html', () => {
|
|
56
|
+
const editor = createEditor({
|
|
57
|
+
content: '<a href="https://hello.world" target="_blank">Hello</a>'
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(editor.getJSON()).toMatchSnapshot();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('should parse block links from html', () => {
|
|
64
|
+
const editor = createEditor({
|
|
65
|
+
content: '<a href="#987654" target="_blank">Hello</a>'
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
expect(editor.getJSON()).toMatchSnapshot();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('should parse link target', () => {
|
|
72
|
+
const editor = createEditor({
|
|
73
|
+
content: '<a href="https://hello.world" target="_blank">Hello</a>'
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
expect(editor.getJSON()).toMatchSnapshot();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('should add default self target for links from html without target', () => {
|
|
80
|
+
const editor = createEditor({
|
|
81
|
+
content: '<a href="https://hello.world">Hello</a>'
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
expect(editor.getJSON()).toMatchSnapshot();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('should parse value for relative url', () => {
|
|
88
|
+
const editor = createEditor({
|
|
89
|
+
content: '<a href="/hello-world">Hello</a>'
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
expect(editor.getJSON()).toMatchSnapshot();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('should parse not valid value', () => {
|
|
96
|
+
const editor = createEditor({
|
|
97
|
+
content: '<a href="934238">Hello</a>'
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
expect(editor.getJSON()).toMatchSnapshot();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -324,6 +324,14 @@ describe('parsing html', () => {
|
|
|
324
324
|
expect(editor.getJSON()).toMatchSnapshot();
|
|
325
325
|
});
|
|
326
326
|
|
|
327
|
+
test('should get both from text with property shorthand', () => {
|
|
328
|
+
const editor = createEditor({
|
|
329
|
+
content: '<p><span style="text-decoration: underline line-through">lorem</span> ipsum</p>'
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
expect(editor.getJSON()).toMatchSnapshot();
|
|
333
|
+
});
|
|
334
|
+
|
|
327
335
|
test('should get both from rendered view', () => {
|
|
328
336
|
const editor = createEditor({
|
|
329
337
|
content: '<p><span style="--zw-text-decoration: underline line-through">lorem</span> ipsum</p>'
|
|
@@ -339,4 +347,20 @@ describe('parsing html', () => {
|
|
|
339
347
|
|
|
340
348
|
expect(editor.getJSON()).toMatchSnapshot();
|
|
341
349
|
});
|
|
350
|
+
|
|
351
|
+
test('should parse underline from tag', () => {
|
|
352
|
+
const editor = createEditor({
|
|
353
|
+
content: '<p><u>lorem</u> ipsum</p>'
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
expect(editor.getJSON()).toMatchSnapshot();
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
test('should parse strike through from tag', () => {
|
|
360
|
+
const editor = createEditor({
|
|
361
|
+
content: '<p><s>lorem</s> ipsum</p>'
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
expect(editor.getJSON()).toMatchSnapshot();
|
|
365
|
+
});
|
|
342
366
|
});
|
|
@@ -278,6 +278,23 @@ Object {
|
|
|
278
278
|
}
|
|
279
279
|
`;
|
|
280
280
|
|
|
281
|
+
exports[`parsing html should ignore invalid value 1`] = `
|
|
282
|
+
Object {
|
|
283
|
+
"content": Array [
|
|
284
|
+
Object {
|
|
285
|
+
"content": Array [
|
|
286
|
+
Object {
|
|
287
|
+
"text": "lorem ipsum",
|
|
288
|
+
"type": "text",
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
"type": "paragraph",
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
"type": "doc",
|
|
295
|
+
}
|
|
296
|
+
`;
|
|
297
|
+
|
|
281
298
|
exports[`parsing html should merge paragraph and text settings 1`] = `
|
|
282
299
|
Object {
|
|
283
300
|
"content": Array [
|