@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.
Files changed (62) hide show
  1. package/config/vite/example.config.js +25 -0
  2. package/dist/wysiwyg.css +1 -1
  3. package/dist/wysiwyg.mjs +3143 -3046
  4. package/example/ExampleApp.vue +5 -0
  5. package/example/{example.html → index.html} +1 -0
  6. package/lib/Wysiwyg.vue +4 -5
  7. package/lib/__tests__/utils/NodeFactory.js +13 -0
  8. package/lib/components/base/__tests__/Modal.test.js +2 -3
  9. package/lib/components/base/composables/__tests__/useValidator.test.js +44 -0
  10. package/lib/components/base/composables/useValidator.js +7 -3
  11. package/lib/components/toolbar/__tests__/Toolbar.test.js +2 -3
  12. package/lib/components/toolbar/controls/StylePresetControl.vue +14 -1
  13. package/lib/components/toolbar/controls/__tests__/StylePresetControl.test.js +16 -0
  14. package/lib/components/toolbar/controls/link/LinkControl.vue +28 -25
  15. package/lib/components/toolbar/controls/link/__tests__/LinkControl.test.js +79 -0
  16. package/lib/components/toolbar/controls/link/__tests__/LinkControlHeader.test.js +42 -0
  17. package/lib/components/toolbar/controls/link/composables/__tests__/__snapshots__/useLink.test.js.snap +8 -0
  18. package/lib/components/toolbar/controls/link/composables/__tests__/useLink.test.js +114 -0
  19. package/lib/components/toolbar/controls/link/destination/LinkControlDestination.vue +2 -2
  20. package/lib/components/toolbar/controls/link/destination/LinkControlUrl.vue +2 -2
  21. package/lib/components/toolbar/controls/link/destination/__tests__/LinkControlPageBlock.test.js +36 -0
  22. package/lib/components/toolbar/controls/link/destination/__tests__/LinkControlUrl.test.js +46 -0
  23. package/lib/components/toolbar/controls/link/destination/__tests__/__snapshots__/LinkControlPageBlock.test.js.snap +9 -0
  24. package/lib/components/toolbar/controls/link/destination/__tests__/__snapshots__/LinkControlUrl.test.js.snap +17 -0
  25. package/lib/composables/useToolbar.js +11 -0
  26. package/lib/directives/__tests__/outClick.test.js +6 -0
  27. package/lib/directives/outClick.js +12 -15
  28. package/lib/enums/Alignments.js +10 -1
  29. package/lib/extensions/Alignment.js +7 -5
  30. package/lib/extensions/FontSize.js +8 -2
  31. package/lib/extensions/FontWeight.js +3 -1
  32. package/lib/extensions/LineHeight.js +30 -36
  33. package/lib/extensions/Link.js +3 -15
  34. package/lib/extensions/TextDecoration.js +18 -0
  35. package/lib/extensions/__tests__/FontSize.test.js +2 -1
  36. package/lib/extensions/__tests__/FontWeight.test.js +8 -0
  37. package/lib/extensions/__tests__/LineHeight.test.js +12 -6
  38. package/lib/extensions/__tests__/Link.test.js +102 -0
  39. package/lib/extensions/__tests__/TextDecoration.test.js +24 -0
  40. package/lib/extensions/__tests__/__snapshots__/FontWeight.test.js.snap +17 -0
  41. package/lib/extensions/__tests__/__snapshots__/Link.test.js.snap +225 -0
  42. package/lib/extensions/__tests__/__snapshots__/TextDecoration.test.js.snap +90 -0
  43. package/lib/extensions/core/plugins/PastePlugin.js +23 -14
  44. package/lib/extensions/index.js +10 -5
  45. package/lib/services/ContentNormalizer.js +55 -15
  46. package/lib/services/__tests__/ContentNormalizer.test.js +39 -4
  47. package/lib/utils/__tests__/convertAlignment.test.js +16 -0
  48. package/lib/utils/__tests__/convertFontSize.test.js +21 -0
  49. package/lib/utils/__tests__/convertLineHeight.test.js +21 -0
  50. package/lib/utils/convertAlignment.js +12 -0
  51. package/lib/utils/convertFontSize.js +8 -0
  52. package/lib/utils/convertLineHeight.js +17 -0
  53. package/lib/utils/index.js +3 -0
  54. package/package.json +3 -13
  55. package/config/webpack/example.config.js +0 -88
  56. package/config/webpack/lib.config.js +0 -43
  57. package/config/webpack/loaders/index.js +0 -6
  58. package/config/webpack/loaders/js-loader.js +0 -5
  59. package/config/webpack/loaders/style-loader.js +0 -9
  60. package/config/webpack/loaders/svg-loader.js +0 -4
  61. package/config/webpack/loaders/vue-loader.js +0 -4
  62. 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,9 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`applying block should apply block 1`] = `
4
+ Array [
5
+ Array [
6
+ 2,
7
+ ],
8
+ ]
9
+ `;
@@ -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 attachListener(onClick) {
6
- if (ContextWindow.window !== window) {
7
- window.document.addEventListener('click', onClick);
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.removeEventListener('click', onClick);
10
+ window.document[action](...args);
15
11
  }
16
- ContextWindow.document.removeEventListener('click', onClick);
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) onOutClick(event);
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(() => attachListener(onClick));
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
- removeListener(dataStorage.get(el).callback);
48
+ toggleListener(false, dataStorage.get(el).callback);
52
49
  }
53
50
  };
@@ -2,5 +2,14 @@ export const Alignments = Object.freeze({
2
2
  LEFT: 'left',
3
3
  CENTER: 'center',
4
4
  RIGHT: 'right',
5
- JUSTIFY: 'justify'
5
+ JUSTIFY: 'justify',
6
+
7
+ get values() {
8
+ return [
9
+ this.LEFT,
10
+ this.CENTER,
11
+ this.RIGHT,
12
+ this.JUSTIFY
13
+ ];
14
+ }
6
15
  });
@@ -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
- if (style.textAlign) {
23
+ const textAlign = convertAlignment(style.textAlign);
24
+
25
+ if (textAlign) {
24
26
  return {
25
- desktop: style.textAlign,
26
- tablet: style.textAlign,
27
- mobile: style.textAlign
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) => value ? String(parseInt(value)) : null;
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
  {
@@ -60,7 +60,9 @@ export const FontWeight = Mark.create({
60
60
  }),
61
61
 
62
62
  parseHTML() {
63
- const getAttrs = (value) => ({ value });
63
+ const getAttrs = (value) => {
64
+ return Number(value) ? { value } : false;
65
+ };
64
66
 
65
67
  return [
66
68
  {
@@ -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
- types: [NodeTypes.PARAGRAPH, NodeTypes.HEADING],
19
- attributes: {
20
- [TextSettings.LINE_HEIGHT]: {
21
- isRequired: false,
15
+ addGlobalAttributes() {
16
+ return [
17
+ {
18
+ types: [NodeTypes.PARAGRAPH, NodeTypes.HEADING],
19
+ attributes: {
20
+ [TextSettings.LINE_HEIGHT]: {
21
+ isRequired: false,
22
22
 
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;
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
- return { mobile, tablet, desktop };
30
- }
31
-
32
- const value = element.style.lineHeight;
29
+ return { mobile, tablet, desktop };
30
+ }
33
31
 
34
- if (!value) return null;
32
+ const value = element.style.lineHeight;
35
33
 
36
- if (!value.includes('px')) {
37
- return { desktop: value, tablet: value, mobile: value };
38
- }
34
+ if (!value) return null;
39
35
 
40
- // element is not connected to window so getComputedStyle is not working on element
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
- return { desktop: relative, tablet: relative, mobile: relative };
46
- },
38
+ return { desktop: converted, tablet: converted, mobile: converted };
39
+ },
47
40
 
48
- renderHTML(attrs) {
49
- if (!attrs.line_height) return null;
41
+ renderHTML(attrs) {
42
+ if (!attrs.line_height) return null;
50
43
 
51
- return renderInlineSetting({
52
- line_height_mobile: attrs.line_height.mobile,
53
- line_height_tablet: attrs.line_height.tablet,
54
- line_height_desktop: attrs.line_height.desktop
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 {
@@ -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: this.options.target,
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
  },
@@ -31,7 +31,8 @@ function createEditor({ content }) {
31
31
  }),
32
32
  FontSize.configure({
33
33
  minSize: 5,
34
- maxSize: 20
34
+ maxSize: 20,
35
+ wrapperRef: ref(document.createElement('div'))
35
36
  })
36
37
  )
37
38
  });
@@ -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({ device: ref('desktop') }),
30
- LineHeight
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
- global.document.body.style.fontSize = '20px';
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 [