@pie-lib/editable-html 11.1.2-next.0 → 11.2.0-beta.2

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 (166) hide show
  1. package/CHANGELOG.md +50 -158
  2. package/NEXT.CHANGELOG.json +1 -0
  3. package/package.json +11 -6
  4. package/src/__tests__/editor.test.jsx +363 -0
  5. package/src/__tests__/serialization.test.js +291 -0
  6. package/src/__tests__/utils.js +36 -0
  7. package/src/block-tags.js +17 -0
  8. package/src/constants.js +7 -0
  9. package/src/editor.jsx +303 -49
  10. package/src/index.jsx +19 -10
  11. package/src/plugins/characters/index.jsx +11 -3
  12. package/src/plugins/characters/utils.js +12 -12
  13. package/src/plugins/css/icons/index.jsx +17 -0
  14. package/src/plugins/css/index.jsx +346 -0
  15. package/src/plugins/customPlugin/index.jsx +85 -0
  16. package/src/plugins/html/index.jsx +9 -6
  17. package/src/plugins/image/__tests__/__snapshots__/component.test.jsx.snap +51 -0
  18. package/src/plugins/image/__tests__/__snapshots__/image-toolbar-logic.test.jsx.snap +27 -0
  19. package/src/plugins/image/__tests__/__snapshots__/image-toolbar.test.jsx.snap +44 -0
  20. package/src/plugins/image/__tests__/component.test.jsx +41 -0
  21. package/src/plugins/image/__tests__/image-toolbar-logic.test.jsx +42 -0
  22. package/src/plugins/image/__tests__/image-toolbar.test.jsx +11 -0
  23. package/src/plugins/image/__tests__/index.test.js +95 -0
  24. package/src/plugins/image/__tests__/insert-image-handler.test.js +113 -0
  25. package/src/plugins/image/__tests__/mock-change.js +15 -0
  26. package/src/plugins/image/index.jsx +2 -1
  27. package/src/plugins/image/insert-image-handler.js +13 -6
  28. package/src/plugins/index.jsx +248 -5
  29. package/src/plugins/list/__tests__/index.test.js +54 -0
  30. package/src/plugins/list/index.jsx +130 -0
  31. package/src/plugins/math/__tests__/__snapshots__/index.test.jsx.snap +48 -0
  32. package/src/plugins/math/__tests__/index.test.jsx +245 -0
  33. package/src/plugins/math/index.jsx +87 -56
  34. package/src/plugins/media/__tests__/index.test.js +75 -0
  35. package/src/plugins/media/index.jsx +3 -2
  36. package/src/plugins/media/media-dialog.js +106 -57
  37. package/src/plugins/rendering/index.js +31 -0
  38. package/src/plugins/respArea/drag-in-the-blank/choice.jsx +4 -1
  39. package/src/plugins/respArea/explicit-constructed-response/index.jsx +10 -8
  40. package/src/plugins/respArea/index.jsx +53 -7
  41. package/src/plugins/respArea/inline-dropdown/index.jsx +13 -6
  42. package/src/plugins/respArea/math-templated/index.jsx +104 -0
  43. package/src/plugins/respArea/utils.jsx +11 -0
  44. package/src/plugins/table/CustomTablePlugin.js +113 -0
  45. package/src/plugins/table/__tests__/__snapshots__/table-toolbar.test.jsx.snap +44 -0
  46. package/src/plugins/table/__tests__/index.test.jsx +401 -0
  47. package/src/plugins/table/__tests__/table-toolbar.test.jsx +42 -0
  48. package/src/plugins/table/index.jsx +46 -59
  49. package/src/plugins/table/table-toolbar.jsx +39 -2
  50. package/src/plugins/textAlign/icons/index.jsx +139 -0
  51. package/src/plugins/textAlign/index.jsx +23 -0
  52. package/src/plugins/toolbar/__tests__/__snapshots__/default-toolbar.test.jsx.snap +923 -0
  53. package/src/plugins/toolbar/__tests__/__snapshots__/editor-and-toolbar.test.jsx.snap +20 -0
  54. package/src/plugins/toolbar/__tests__/__snapshots__/toolbar-buttons.test.jsx.snap +36 -0
  55. package/src/plugins/toolbar/__tests__/__snapshots__/toolbar.test.jsx.snap +46 -0
  56. package/src/plugins/toolbar/__tests__/default-toolbar.test.jsx +94 -0
  57. package/src/plugins/toolbar/__tests__/editor-and-toolbar.test.jsx +37 -0
  58. package/src/plugins/toolbar/__tests__/toolbar-buttons.test.jsx +51 -0
  59. package/src/plugins/toolbar/__tests__/toolbar.test.jsx +106 -0
  60. package/src/plugins/toolbar/default-toolbar.jsx +82 -20
  61. package/src/plugins/toolbar/done-button.jsx +3 -1
  62. package/src/plugins/toolbar/editor-and-toolbar.jsx +18 -13
  63. package/src/plugins/toolbar/toolbar-buttons.jsx +52 -11
  64. package/src/plugins/toolbar/toolbar.jsx +31 -8
  65. package/src/serialization.jsx +213 -38
  66. package/README.md +0 -45
  67. package/deploy.sh +0 -16
  68. package/lib/editor.js +0 -1094
  69. package/lib/editor.js.map +0 -1
  70. package/lib/index.js +0 -253
  71. package/lib/index.js.map +0 -1
  72. package/lib/parse-html.js +0 -16
  73. package/lib/parse-html.js.map +0 -1
  74. package/lib/plugins/characters/custom-popper.js +0 -73
  75. package/lib/plugins/characters/custom-popper.js.map +0 -1
  76. package/lib/plugins/characters/index.js +0 -300
  77. package/lib/plugins/characters/index.js.map +0 -1
  78. package/lib/plugins/characters/utils.js +0 -381
  79. package/lib/plugins/characters/utils.js.map +0 -1
  80. package/lib/plugins/html/icons/index.js +0 -38
  81. package/lib/plugins/html/icons/index.js.map +0 -1
  82. package/lib/plugins/html/index.js +0 -76
  83. package/lib/plugins/html/index.js.map +0 -1
  84. package/lib/plugins/image/alt-dialog.js +0 -129
  85. package/lib/plugins/image/alt-dialog.js.map +0 -1
  86. package/lib/plugins/image/component.js +0 -419
  87. package/lib/plugins/image/component.js.map +0 -1
  88. package/lib/plugins/image/image-toolbar.js +0 -177
  89. package/lib/plugins/image/image-toolbar.js.map +0 -1
  90. package/lib/plugins/image/index.js +0 -262
  91. package/lib/plugins/image/index.js.map +0 -1
  92. package/lib/plugins/image/insert-image-handler.js +0 -152
  93. package/lib/plugins/image/insert-image-handler.js.map +0 -1
  94. package/lib/plugins/index.js +0 -143
  95. package/lib/plugins/index.js.map +0 -1
  96. package/lib/plugins/list/index.js +0 -204
  97. package/lib/plugins/list/index.js.map +0 -1
  98. package/lib/plugins/math/index.js +0 -419
  99. package/lib/plugins/math/index.js.map +0 -1
  100. package/lib/plugins/media/index.js +0 -384
  101. package/lib/plugins/media/index.js.map +0 -1
  102. package/lib/plugins/media/media-dialog.js +0 -668
  103. package/lib/plugins/media/media-dialog.js.map +0 -1
  104. package/lib/plugins/media/media-toolbar.js +0 -101
  105. package/lib/plugins/media/media-toolbar.js.map +0 -1
  106. package/lib/plugins/media/media-wrapper.js +0 -93
  107. package/lib/plugins/media/media-wrapper.js.map +0 -1
  108. package/lib/plugins/respArea/drag-in-the-blank/choice.js +0 -251
  109. package/lib/plugins/respArea/drag-in-the-blank/choice.js.map +0 -1
  110. package/lib/plugins/respArea/drag-in-the-blank/index.js +0 -97
  111. package/lib/plugins/respArea/drag-in-the-blank/index.js.map +0 -1
  112. package/lib/plugins/respArea/explicit-constructed-response/index.js +0 -55
  113. package/lib/plugins/respArea/explicit-constructed-response/index.js.map +0 -1
  114. package/lib/plugins/respArea/icons/index.js +0 -95
  115. package/lib/plugins/respArea/icons/index.js.map +0 -1
  116. package/lib/plugins/respArea/index.js +0 -293
  117. package/lib/plugins/respArea/index.js.map +0 -1
  118. package/lib/plugins/respArea/inline-dropdown/index.js +0 -70
  119. package/lib/plugins/respArea/inline-dropdown/index.js.map +0 -1
  120. package/lib/plugins/respArea/utils.js +0 -110
  121. package/lib/plugins/respArea/utils.js.map +0 -1
  122. package/lib/plugins/table/icons/index.js +0 -69
  123. package/lib/plugins/table/icons/index.js.map +0 -1
  124. package/lib/plugins/table/index.js +0 -499
  125. package/lib/plugins/table/index.js.map +0 -1
  126. package/lib/plugins/table/table-toolbar.js +0 -158
  127. package/lib/plugins/table/table-toolbar.js.map +0 -1
  128. package/lib/plugins/toolbar/default-toolbar.js +0 -174
  129. package/lib/plugins/toolbar/default-toolbar.js.map +0 -1
  130. package/lib/plugins/toolbar/done-button.js +0 -50
  131. package/lib/plugins/toolbar/done-button.js.map +0 -1
  132. package/lib/plugins/toolbar/editor-and-toolbar.js +0 -287
  133. package/lib/plugins/toolbar/editor-and-toolbar.js.map +0 -1
  134. package/lib/plugins/toolbar/index.js +0 -34
  135. package/lib/plugins/toolbar/index.js.map +0 -1
  136. package/lib/plugins/toolbar/toolbar-buttons.js +0 -161
  137. package/lib/plugins/toolbar/toolbar-buttons.js.map +0 -1
  138. package/lib/plugins/toolbar/toolbar.js +0 -352
  139. package/lib/plugins/toolbar/toolbar.js.map +0 -1
  140. package/lib/plugins/utils.js +0 -62
  141. package/lib/plugins/utils.js.map +0 -1
  142. package/lib/serialization.js +0 -488
  143. package/lib/serialization.js.map +0 -1
  144. package/lib/theme.js +0 -9
  145. package/lib/theme.js.map +0 -1
  146. package/playground/image/data.js +0 -59
  147. package/playground/image/index.html +0 -22
  148. package/playground/image/index.jsx +0 -81
  149. package/playground/index.html +0 -25
  150. package/playground/mathquill/index.html +0 -22
  151. package/playground/mathquill/index.jsx +0 -155
  152. package/playground/package.json +0 -15
  153. package/playground/prod-test/index.html +0 -22
  154. package/playground/prod-test/index.jsx +0 -28
  155. package/playground/schema-override/data.js +0 -29
  156. package/playground/schema-override/image-plugin.jsx +0 -41
  157. package/playground/schema-override/index.html +0 -21
  158. package/playground/schema-override/index.jsx +0 -97
  159. package/playground/serialization/data.js +0 -29
  160. package/playground/serialization/image-plugin.jsx +0 -41
  161. package/playground/serialization/index.html +0 -22
  162. package/playground/serialization/index.jsx +0 -12
  163. package/playground/static.json +0 -3
  164. package/playground/table-examples.html +0 -70
  165. package/playground/webpack.config.js +0 -42
  166. package/static.json +0 -1
@@ -0,0 +1,20 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`toolbar renders 1`] = `
4
+ <div
5
+ className="root"
6
+ >
7
+ <div
8
+ className="editorHolder editorInFocus"
9
+ >
10
+ <div
11
+ className=""
12
+ >
13
+ children
14
+ </div>
15
+ </div>
16
+ <div>
17
+ ---- toolbar ------
18
+ </div>
19
+ </div>
20
+ `;
@@ -0,0 +1,36 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Button renders 1`] = `
4
+ <button
5
+ className=""
6
+ onKeyDown={[Function]}
7
+ onMouseDown={[Function]}
8
+ tabIndex={0}
9
+ >
10
+ children
11
+ </button>
12
+ `;
13
+
14
+ exports[`MarkButton renders active 1`] = `
15
+ <button
16
+ aria-pressed={true}
17
+ className="button active"
18
+ onKeyDown={[Function]}
19
+ onMouseDown={[Function]}
20
+ tabIndex={0}
21
+ >
22
+ children
23
+ </button>
24
+ `;
25
+
26
+ exports[`MarkButton renders not active 1`] = `
27
+ <button
28
+ aria-pressed={false}
29
+ className="button"
30
+ onKeyDown={[Function]}
31
+ onMouseDown={[Function]}
32
+ tabIndex={0}
33
+ >
34
+ children
35
+ </button>
36
+ `;
@@ -0,0 +1,46 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`toolbar default renders default toolbar 1`] = `
4
+ <div
5
+ className="toolbar pie-toolbar"
6
+ onClick={[Function]}
7
+ style={Object {}}
8
+ >
9
+ <div
10
+ className="DefaultToolbar-defaultToolbar-1"
11
+ onFocus={[Function]}
12
+ tabIndex="1"
13
+ >
14
+ <div
15
+ className="DefaultToolbar-buttonsContainer-2"
16
+ />
17
+ <div
18
+ ariaLabel="Done"
19
+ className="RawDoneButton-iconRoot-3"
20
+ />
21
+ </div>
22
+ <div
23
+ className="shared"
24
+ />
25
+ </div>
26
+ `;
27
+
28
+ exports[`toolbar renders custom toolbar 1`] = `
29
+ <div
30
+ className="toolbar pie-toolbar"
31
+ onClick={[Function]}
32
+ style={Object {}}
33
+ >
34
+ <div>
35
+ --------- custom toolbar -----------
36
+ </div>
37
+ <div
38
+ className="shared"
39
+ >
40
+ <div
41
+ ariaLabel="Delete"
42
+ className="iconRoot"
43
+ />
44
+ </div>
45
+ </div>
46
+ `;
@@ -0,0 +1,94 @@
1
+ import { classObject, mockIconButton, mockMathInput } from '../../../__tests__/utils';
2
+ import { shallow } from 'enzyme';
3
+ import React from 'react';
4
+
5
+ import { Data, Value, Inline } from 'slate';
6
+ import { DefaultToolbar, ToolbarButton } from '../default-toolbar';
7
+ import { DoneButton } from '../done-button';
8
+ import debug from 'debug';
9
+ import renderer from 'react-test-renderer';
10
+
11
+ mockMathInput();
12
+
13
+ jest.mock('@material-ui/core/IconButton', () => {
14
+ return (props) => <div className={props.className} style={props.style} ariaLabel={props['aria-label']} />;
15
+ });
16
+
17
+ let node = Inline.fromJSON({ type: 'i' });
18
+ let value;
19
+ const log = debug('@pie-lib:editable-html:test:toolbar');
20
+
21
+ describe('default-toolbar', () => {
22
+ let w;
23
+ let onDone = jest.fn();
24
+ let onChange = jest.fn();
25
+ const wrapper = (extras) => {
26
+ const defaults = {
27
+ classes: {},
28
+ value: Value.fromJSON({}),
29
+ plugins: [],
30
+ className: 'className',
31
+ onDone,
32
+ onChange,
33
+ deletable: false,
34
+ showDone: true,
35
+ };
36
+ const props = { ...defaults, ...extras };
37
+ return shallow(<DefaultToolbar {...props} />);
38
+ };
39
+
40
+ describe('snapshot', () => {
41
+ it('renders', () => {
42
+ expect(w).toMatchSnapshot();
43
+ });
44
+ it('renders 1 plugins', () => {
45
+ w = wrapper({
46
+ plugins: [{ toolbar: {}, name: 'plugin-one' }],
47
+ });
48
+ expect(w).toMatchSnapshot();
49
+ expect(w.find(ToolbarButton)).toHaveLength(1);
50
+ });
51
+ it('renders 2 plugins', () => {
52
+ w = wrapper({
53
+ plugins: [
54
+ { toolbar: {}, name: 'plugin-one' },
55
+ { toolbar: {}, name: 'plugin-two' },
56
+ ],
57
+ });
58
+ expect(w).toMatchSnapshot();
59
+ expect(w.find(ToolbarButton)).toHaveLength(2);
60
+ });
61
+ it('renders 1 plugins, 1 is disabled', () => {
62
+ w = wrapper({
63
+ pluginProps: {
64
+ 'plugin-one': {
65
+ disabled: true,
66
+ },
67
+ },
68
+ plugins: [
69
+ { toolbar: {}, name: 'plugin-one' },
70
+ { toolbar: {}, name: 'plugin-two' },
71
+ ],
72
+ });
73
+ expect(w).toMatchSnapshot();
74
+ expect(w.find(ToolbarButton)).toHaveLength(1);
75
+ });
76
+ it('renders without done button', () => {
77
+ w = wrapper({
78
+ deletable: false,
79
+ });
80
+
81
+ expect(w).toMatchSnapshot();
82
+ expect(w.find(DoneButton)).toHaveLength(1);
83
+ });
84
+ it('renders with done button', () => {
85
+ w = wrapper({
86
+ deletable: true,
87
+ });
88
+
89
+ expect(w).toMatchSnapshot();
90
+ expect(w.find(DoneButton)).toHaveLength(0);
91
+ });
92
+ });
93
+ describe('logic', () => {});
94
+ });
@@ -0,0 +1,37 @@
1
+ import { classObject, mockIconButton } from '../../../__tests__/utils';
2
+
3
+ import { Data, Value } from 'slate';
4
+ import { EditorAndToolbar, getClonedChildren } from '../editor-and-toolbar';
5
+ import React from 'react';
6
+ import debug from 'debug';
7
+ import renderer from 'react-test-renderer';
8
+
9
+ jest.mock('../toolbar', () => () => <div>---- toolbar ------ </div>);
10
+
11
+ mockIconButton();
12
+
13
+ const log = debug('@pie-lib:editable-html:test:editor-and-toolbar');
14
+
15
+ describe('toolbar', () => {
16
+ let onDelete, classes;
17
+
18
+ beforeEach(() => {
19
+ onDelete = jest.fn();
20
+ classes = classObject('root', 'editorHolder', 'editorInFocus');
21
+ });
22
+
23
+ it('renders', () => {
24
+ const value = Value.fromJSON({});
25
+ Object.defineProperty(value, 'isFocused', { get: jest.fn(() => true) });
26
+
27
+ const tree = renderer
28
+ .create(
29
+ <EditorAndToolbar classes={classes} value={value} plugins={[]} onDone={jest.fn()} onChange={jest.fn()}>
30
+ children
31
+ </EditorAndToolbar>,
32
+ )
33
+ .toJSON();
34
+ log('tree: ', JSON.stringify(tree, null, ' '));
35
+ expect(tree).toMatchSnapshot();
36
+ });
37
+ });
@@ -0,0 +1,51 @@
1
+ import { classObject, mockIconButton } from '../../../__tests__/utils';
2
+
3
+ import { Data } from 'slate';
4
+ import { RawMarkButton, RawButton } from '../toolbar-buttons';
5
+ import React from 'react';
6
+ import debug from 'debug';
7
+ import renderer from 'react-test-renderer';
8
+
9
+ mockIconButton();
10
+
11
+ const log = debug('@pie-lib:editable-html:test:editor-and-toolbar');
12
+
13
+ describe('Button', () => {
14
+ it('renders', () => {
15
+ const classes = classObject('root');
16
+ const tree = renderer
17
+ .create(
18
+ <RawButton onClick={jest.fn()} classes={classes}>
19
+ children
20
+ </RawButton>,
21
+ )
22
+ .toJSON();
23
+ expect(tree).toMatchSnapshot();
24
+ });
25
+ });
26
+
27
+ describe('MarkButton', () => {
28
+ const classes = classObject('button', 'root', 'active');
29
+
30
+ it('renders not active', () => {
31
+ const tree = renderer
32
+ .create(
33
+ <RawMarkButton mark={'i'} onToggle={jest.fn()} active={false} classes={classes}>
34
+ children
35
+ </RawMarkButton>,
36
+ )
37
+ .toJSON();
38
+ expect(tree).toMatchSnapshot();
39
+ });
40
+
41
+ it('renders active', () => {
42
+ const tree = renderer
43
+ .create(
44
+ <RawMarkButton mark={'i'} onToggle={jest.fn()} active={true} classes={classes}>
45
+ children
46
+ </RawMarkButton>,
47
+ )
48
+ .toJSON();
49
+ expect(tree).toMatchSnapshot();
50
+ });
51
+ });
@@ -0,0 +1,106 @@
1
+ import { classObject, mockIconButton, mockMathInput } from '../../../__tests__/utils';
2
+ import { shallow } from 'enzyme';
3
+
4
+ import { Data, Value, Inline } from 'slate';
5
+ import { Toolbar, DefaultToolbar } from '../toolbar';
6
+ import React from 'react';
7
+ import debug from 'debug';
8
+ import renderer from 'react-test-renderer';
9
+
10
+ mockMathInput();
11
+
12
+ jest.mock('@material-ui/core/IconButton', () => {
13
+ return (props) => <div className={props.className} style={props.style} ariaLabel={props['aria-label']} />;
14
+ });
15
+
16
+ let node = Inline.fromJSON({ type: 'i' });
17
+ let parentNode = Inline.fromJSON({
18
+ type: 'i',
19
+ });
20
+ let value;
21
+ const log = debug('@pie-lib:editable-html:test:toolbar');
22
+
23
+ describe('toolbar', () => {
24
+ let onDelete, classes, document, toolbarOpts;
25
+
26
+ beforeEach(() => {
27
+ onDelete = jest.fn();
28
+
29
+ toolbarOpts = {
30
+ position: 'bottom',
31
+ alwaysVisible: false,
32
+ };
33
+
34
+ value = Value.fromJSON({});
35
+ document = {
36
+ getClosestInline: jest.fn().mockReturnValue(node),
37
+ getParent: jest.fn().mockReturnValue(),
38
+ };
39
+
40
+ Object.defineProperties(value, {
41
+ isCollapsed: {
42
+ get: jest.fn(() => true),
43
+ },
44
+ startKey: {
45
+ get: jest.fn(() => '1'),
46
+ },
47
+ document: {
48
+ get: jest.fn(() => document),
49
+ },
50
+ });
51
+
52
+ classes = classObject('iconRoot', 'inline', 'toolbar', 'focused', 'shared', 'inline', 'pie-toolbar');
53
+ });
54
+
55
+ test('renders custom toolbar', () => {
56
+ const plugins = [
57
+ {
58
+ deleteNode: () => true,
59
+ toolbar: {
60
+ supports: () => true,
61
+ customToolbar: () => () => <div> --------- custom toolbar ----------- </div>,
62
+ },
63
+ },
64
+ ];
65
+
66
+ const tree = renderer
67
+ .create(
68
+ <Toolbar
69
+ toolbarOpts={toolbarOpts}
70
+ plugins={plugins}
71
+ classes={classes}
72
+ value={value}
73
+ onDone={jest.fn()}
74
+ onChange={jest.fn()}
75
+ />,
76
+ )
77
+ .toJSON();
78
+
79
+ log('tree: ', JSON.stringify(tree, null, ' '));
80
+ expect(tree).toMatchSnapshot();
81
+ });
82
+
83
+ describe('default', () => {
84
+ let plugins;
85
+
86
+ beforeEach(() => {
87
+ plugins = [];
88
+ });
89
+
90
+ test('renders default toolbar', () => {
91
+ const tree = renderer
92
+ .create(
93
+ <Toolbar
94
+ toolbarOpts={toolbarOpts}
95
+ plugins={plugins}
96
+ classes={classes}
97
+ value={value}
98
+ onDone={jest.fn()}
99
+ onChange={jest.fn()}
100
+ />,
101
+ )
102
+ .toJSON();
103
+ expect(tree).toMatchSnapshot();
104
+ });
105
+ });
106
+ });
@@ -8,7 +8,6 @@ import { withStyles } from '@material-ui/core/styles';
8
8
 
9
9
  import { Button, MarkButton } from './toolbar-buttons';
10
10
  import debug from 'debug';
11
- import { is } from 'immutable';
12
11
 
13
12
  const log = debug('@pie-lib:editable-html:plugins:toolbar');
14
13
 
@@ -21,31 +20,45 @@ export const ToolbarButton = (props) => {
21
20
 
22
21
  if (props.isMark) {
23
22
  const isActive = hasMark(props.value, props.type);
23
+ const fnToCall =
24
+ props.type === 'css' ? () => props.onClick(props.value, props.onChange, props.getFocusedValue) : onToggle;
24
25
 
25
26
  log('[ToolbarButton] mark:isActive: ', isActive);
26
27
 
27
- return (
28
- <MarkButton active={isActive} label={props.type} onToggle={onToggle} mark={props.type}>
29
- {props.icon}
30
- </MarkButton>
31
- );
32
- } else {
33
- const { disabled } = props;
34
- const isActive = props.isActive ? props.isActive(props.value, props.type) : hasBlock(props.value, props.type);
28
+ let ariaLabel;
35
29
 
36
- log('[ToolbarButton] block:isActive: ', isActive);
30
+ if (props.type === 'sup') {
31
+ ariaLabel = 'Superscript (marks text as superscripted)';
32
+ } else if (props.type === 'sub') {
33
+ ariaLabel = 'Subscript (marks text as subscripted)';
34
+ } else {
35
+ ariaLabel = props.type;
36
+ }
37
37
 
38
38
  return (
39
- <Button
40
- active={isActive}
41
- disabled={disabled}
42
- onClick={() => props.onClick(props.value, props.onChange, props.getFocusedValue)}
43
- extraStyles={props.buttonStyles}
44
- >
39
+ <MarkButton active={isActive} onToggle={fnToCall} mark={props.type} label={ariaLabel}>
45
40
  {props.icon}
46
- </Button>
41
+ </MarkButton>
47
42
  );
48
43
  }
44
+
45
+ const { disabled } = props;
46
+ const isActive = props.isActive ? props.isActive(props.value, props.type) : hasBlock(props.value, props.type);
47
+
48
+ log('[ToolbarButton] block:isActive: ', isActive);
49
+ const newIcon = React.cloneElement(props.icon);
50
+
51
+ return (
52
+ <Button
53
+ ariaLabel={props.ariaLabel}
54
+ active={isActive}
55
+ disabled={disabled}
56
+ onClick={(event) => props.onClick(props.value, props.onChange, props.getFocusedValue, event)}
57
+ extraStyles={props.buttonStyles}
58
+ >
59
+ {newIcon}
60
+ </Button>
61
+ );
49
62
  };
50
63
 
51
64
  ToolbarButton.propTypes = {
@@ -60,6 +73,7 @@ ToolbarButton.propTypes = {
60
73
  onClick: PropTypes.func,
61
74
  type: PropTypes.string,
62
75
  value: PropTypes.object,
76
+ ariaLabel: PropTypes.string,
63
77
  };
64
78
 
65
79
  const isActiveToolbarPlugin = (props) => (plugin) => {
@@ -79,6 +93,9 @@ export const DefaultToolbar = ({
79
93
  showDone,
80
94
  deletable,
81
95
  isHtmlMode,
96
+ doneButtonRef,
97
+ onBlur,
98
+ onFocus,
82
99
  }) => {
83
100
  pluginProps = {
84
101
  // disable HTML plugin by default, at least for now
@@ -87,6 +104,15 @@ export const DefaultToolbar = ({
87
104
  };
88
105
  let filtered;
89
106
 
107
+ const handleFocus = (event) => {
108
+ const doneButtonClassName = doneButtonRef?.current?.className;
109
+ const isRawDoneButton = doneButtonClassName && event.target?.closest(`[class*="${doneButtonClassName}"]`);
110
+
111
+ if (onFocus && !isRawDoneButton) {
112
+ onFocus(event);
113
+ }
114
+ };
115
+
90
116
  if (isHtmlMode) {
91
117
  filtered = plugins
92
118
  .filter((plugin) => {
@@ -97,16 +123,48 @@ export const DefaultToolbar = ({
97
123
  filtered = plugins.filter(isActiveToolbarPlugin(pluginProps)).map((p) => p.toolbar);
98
124
  }
99
125
 
126
+ const isListActive = plugins.some(
127
+ (plugin) =>
128
+ isActiveToolbarPlugin(pluginProps)(plugin) &&
129
+ ['ul_list', 'ol_list'].includes(plugin.name) &&
130
+ plugin.toolbar.isActive(value, plugin.name),
131
+ );
132
+
133
+ const isTableActive = plugins.some(
134
+ (plugin) =>
135
+ isActiveToolbarPlugin(pluginProps)(plugin) &&
136
+ plugin.name === 'table' &&
137
+ plugin.utils &&
138
+ plugin.utils.isSelectionInTable &&
139
+ plugin.utils.isSelectionInTable(value),
140
+ );
141
+
142
+ const isToolbarButtonDisabled = (plugin) => {
143
+ if (plugin.type === 'table') {
144
+ return isListActive;
145
+ } else if (plugin.type === 'ul_list' || plugin.type === 'ol_list') {
146
+ return isTableActive;
147
+ }
148
+ return plugin.disabled;
149
+ };
150
+
100
151
  return (
101
- <div className={classes.defaultToolbar}>
152
+ <div className={classes.defaultToolbar} onFocus={handleFocus} tabIndex="1" onBlur={onBlur}>
102
153
  <div className={classes.buttonsContainer}>
103
154
  {filtered.map((p, index) => {
104
155
  return (
105
- <ToolbarButton {...p} key={index} value={value} onChange={onChange} getFocusedValue={getFocusedValue} />
156
+ <ToolbarButton
157
+ {...p}
158
+ key={index}
159
+ value={value}
160
+ onChange={onChange}
161
+ getFocusedValue={getFocusedValue}
162
+ disabled={isToolbarButtonDisabled(p)}
163
+ />
106
164
  );
107
165
  })}
108
166
  </div>
109
- {showDone && !deletable && !isHtmlMode && <DoneButton onClick={onDone} />}
167
+ {showDone && !deletable && <DoneButton doneButtonRef={doneButtonRef} onClick={onDone} />}
110
168
  </div>
111
169
  );
112
170
  };
@@ -122,6 +180,10 @@ DefaultToolbar.propTypes = {
122
180
  showDone: PropTypes.bool,
123
181
  addArea: PropTypes.bool,
124
182
  deletable: PropTypes.bool,
183
+ isHtmlMode: PropTypes.bool,
184
+ doneButtonRef: PropTypes.func,
185
+ onBlur: PropTypes.func,
186
+ onFocus: PropTypes.func,
125
187
  };
126
188
 
127
189
  DefaultToolbar.defaultProps = {
@@ -5,10 +5,11 @@ import Check from '@material-ui/icons/Check';
5
5
  import { withStyles } from '@material-ui/core/styles';
6
6
  import PropTypes from 'prop-types';
7
7
 
8
- export const RawDoneButton = ({ classes, onClick }) => (
8
+ export const RawDoneButton = ({ classes, onClick, doneButtonRef }) => (
9
9
  <IconButton
10
10
  aria-label="Done"
11
11
  className={classes.iconRoot}
12
+ buttonRef={doneButtonRef}
12
13
  onClick={onClick}
13
14
  classes={{
14
15
  label: classes.label,
@@ -22,6 +23,7 @@ export const RawDoneButton = ({ classes, onClick }) => (
22
23
  RawDoneButton.propTypes = {
23
24
  classes: PropTypes.object.isRequired,
24
25
  onClick: PropTypes.func,
26
+ doneButtonRef: PropTypes.func,
25
27
  };
26
28
 
27
29
  const styles = {
@@ -6,7 +6,6 @@ import { primary } from '../../theme';
6
6
  import { withStyles } from '@material-ui/core/styles';
7
7
  import PropTypes from 'prop-types';
8
8
  import SlatePropTypes from 'slate-prop-types';
9
- import { IS_FIREFOX } from 'slate-dev-environment';
10
9
  import { color } from '@pie-lib/render-ui';
11
10
 
12
11
  const log = debug('@pie-lib:editable-html:plugins:toolbar:editor-and-toolbar');
@@ -33,18 +32,14 @@ export class EditorAndToolbar extends React.Component {
33
32
  alwaysVisible: PropTypes.bool,
34
33
  error: PropTypes.string,
35
34
  noBorder: PropTypes.any,
35
+ noPadding: PropTypes.any,
36
36
  }),
37
+ focusToolbar: PropTypes.bool.isRequired,
38
+ onToolbarFocus: PropTypes.func.isRequired,
39
+ onToolbarBlur: PropTypes.func.isRequired,
40
+ doneButtonRef: PropTypes.func,
37
41
  };
38
42
 
39
- /** This is an interim fix until this PR is merged in slate:
40
- * https://github.com/ianstormtaylor/slate/pull/2236
41
- */
42
- componentDidMount() {
43
- if (IS_FIREFOX) {
44
- this.editorRef.tmp.isUpdatingSelection = true;
45
- }
46
- }
47
-
48
43
  render() {
49
44
  const {
50
45
  classes,
@@ -63,15 +58,21 @@ export class EditorAndToolbar extends React.Component {
63
58
  toolbarOpts,
64
59
  onDataChange,
65
60
  toolbarRef,
61
+ doneButtonRef,
62
+ focusToolbar,
63
+ onToolbarFocus,
64
+ onToolbarBlur,
66
65
  } = this.props;
67
66
 
68
- const inFocus = value.isFocused || (focusedNode !== null && focusedNode !== undefined);
67
+ let inFocus = value.isFocused || (focusedNode !== null && focusedNode !== undefined) || focusToolbar;
68
+
69
69
  const holderNames = classNames(classes.editorHolder, {
70
70
  [classes.editorInFocus]: inFocus,
71
71
  [classes.readOnly]: readOnly,
72
72
  [classes.disabledUnderline]: disableUnderline,
73
73
  [classes.disabledScrollbar]: disableScrollbar,
74
74
  });
75
+
75
76
  let clonedChildren = children;
76
77
 
77
78
  if (typeof children !== 'string') {
@@ -96,7 +97,7 @@ export class EditorAndToolbar extends React.Component {
96
97
  <div
97
98
  className={classNames(
98
99
  {
99
- [classes.noPadding]: toolbarOpts && toolbarOpts.noBorder,
100
+ [classes.noPadding]: toolbarOpts && toolbarOpts.noPadding,
100
101
  },
101
102
  classes.children,
102
103
  )}
@@ -104,17 +105,21 @@ export class EditorAndToolbar extends React.Component {
104
105
  {clonedChildren}
105
106
  </div>
106
107
  </div>
108
+
107
109
  <Toolbar
108
110
  autoWidth={autoWidth}
109
111
  plugins={plugins}
110
112
  focusedNode={focusedNode}
111
113
  value={value}
112
114
  isFocused={inFocus}
115
+ onBlur={onToolbarBlur}
116
+ onFocus={onToolbarFocus}
113
117
  onChange={onChange}
114
118
  getFocusedValue={getFocusedValue}
115
119
  onDone={onDone}
116
120
  onDataChange={onDataChange}
117
121
  toolbarRef={toolbarRef}
122
+ doneButtonRef={doneButtonRef}
118
123
  pluginProps={pluginProps}
119
124
  toolbarOpts={toolbarOpts}
120
125
  />
@@ -144,7 +149,7 @@ const style = (theme) => ({
144
149
  editorHolder: {
145
150
  position: 'relative',
146
151
  padding: '0px',
147
- overflowY: 'scroll',
152
+ overflowY: 'auto',
148
153
  color: color.text(),
149
154
  backgroundColor: color.background(),
150
155
  '&::before': {