@pie-lib/editable-html-tip-tap 2.1.19 → 2.1.20-beta.10
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/CHANGELOG.md +0 -130
- package/lib/__tests__/EditableHtml.test.js +831 -0
- package/lib/__tests__/constants.test.js +21 -0
- package/lib/__tests__/div-to-paragraph-conversion.test.js +213 -0
- package/lib/__tests__/extensions.test.js +209 -0
- package/lib/__tests__/index.test.js +250 -0
- package/lib/__tests__/size-utils.test.js +57 -0
- package/lib/__tests__/spellcheck.test.js +142 -0
- package/lib/__tests__/theme.test.js +17 -0
- package/lib/components/EditableHtml.js +0 -56
- package/lib/components/EditableHtml.js.map +1 -1
- package/lib/components/MenuBar.js +12 -81
- package/lib/components/MenuBar.js.map +1 -1
- package/lib/components/TiptapContainer.js +2 -2
- package/lib/components/TiptapContainer.js.map +1 -1
- package/lib/components/__tests__/AltDialog.test.js +201 -0
- package/lib/components/__tests__/CharacterPicker.test.js +455 -0
- package/lib/components/__tests__/CssIcon.test.js +58 -0
- package/lib/components/__tests__/DragInTheBlank.test.js +295 -0
- package/lib/components/__tests__/ExplicitConstructedResponse.test.js +345 -0
- package/lib/components/__tests__/ImageToolbar.test.js +185 -0
- package/lib/components/__tests__/InlineDropdown.test.js +839 -0
- package/lib/components/__tests__/InsertImageHandler.test.js +171 -0
- package/lib/components/__tests__/MediaDialog.test.js +433 -0
- package/lib/components/__tests__/MediaToolbar.test.js +126 -0
- package/lib/components/__tests__/MediaWrapper.test.js +96 -0
- package/lib/components/__tests__/MenuBar.test.js +502 -0
- package/lib/components/__tests__/RespArea.test.js +171 -0
- package/lib/components/__tests__/TableIcons.test.js +153 -0
- package/lib/components/__tests__/TextAlign.test.js +209 -0
- package/lib/components/__tests__/TiptapContainer.test.js +196 -0
- package/lib/components/__tests__/characterUtils.test.js +178 -0
- package/lib/components/__tests__/choice.test.js +213 -0
- package/lib/components/__tests__/custom-popper.test.js +108 -0
- package/lib/components/__tests__/done-button.test.js +72 -0
- package/lib/components/__tests__/toolbar-buttons.test.js +277 -0
- package/lib/components/common/toolbar-buttons.js +5 -6
- package/lib/components/common/toolbar-buttons.js.map +1 -1
- package/lib/components/icons/TableIcons.js +1 -3
- package/lib/components/icons/TableIcons.js.map +1 -1
- package/lib/components/icons/TextAlign.js +5 -14
- package/lib/components/icons/TextAlign.js.map +1 -1
- package/lib/components/respArea/DragInTheBlank/__tests__/choice.test.js +90 -0
- package/lib/components/respArea/MathTemplated.js +8 -14
- package/lib/components/respArea/MathTemplated.js.map +1 -1
- package/lib/components/respArea/__tests__/MathTemplated.test.js +217 -0
- package/lib/extensions/__tests__/css.test.js +214 -0
- package/lib/extensions/__tests__/custom-toolbar-wrapper.test.js +175 -0
- package/lib/extensions/__tests__/divNode.test.js +108 -0
- package/lib/extensions/__tests__/ensure-empty-root-div.test.js +50 -0
- package/lib/extensions/__tests__/ensure-list-item-content-is-div.test.js +60 -0
- package/lib/extensions/__tests__/extended-list-item.test.js +13 -0
- package/lib/extensions/__tests__/extended-table-cell.test.js +15 -0
- package/lib/extensions/__tests__/extended-table.test.js +200 -0
- package/lib/extensions/__tests__/image-component.test.js +432 -0
- package/lib/extensions/__tests__/image.test.js +313 -0
- package/lib/extensions/__tests__/math.test.js +1411 -0
- package/lib/extensions/__tests__/media-node-view.test.js +307 -0
- package/lib/extensions/__tests__/media.test.js +292 -0
- package/lib/extensions/__tests__/responseArea.test.js +736 -0
- package/lib/extensions/math.js +37 -19
- package/lib/extensions/math.js.map +1 -1
- package/lib/styles/editorContainerStyles.js +2 -2
- package/lib/styles/editorContainerStyles.js.map +1 -1
- package/lib/utils/__tests__/helper.test.js +107 -0
- package/lib/utils/__tests__/toolbar.test.js +38 -0
- package/package.json +9 -9
- package/src/__tests__/EditableHtml.test.jsx +0 -80
- package/src/components/EditableHtml.jsx +0 -52
- package/src/components/MenuBar.jsx +9 -91
- package/src/components/TiptapContainer.jsx +2 -2
- package/src/components/__tests__/MenuBar.test.jsx +9 -97
- package/src/components/__tests__/toolbar-buttons.test.jsx +2 -15
- package/src/components/common/toolbar-buttons.jsx +4 -6
- package/src/components/icons/TableIcons.jsx +1 -9
- package/src/components/icons/TextAlign.jsx +5 -39
- package/src/components/respArea/MathTemplated.jsx +6 -13
- package/src/components/respArea/__tests__/MathTemplated.test.jsx +1 -1
- package/src/extensions/__tests__/math.test.js +11 -9
- package/src/extensions/math.js +19 -11
- package/src/styles/editorContainerStyles.js +2 -2
- package/LICENSE.md +0 -5
- package/src/__tests__/editor-attributes.test.jsx +0 -77
- package/src/__tests__/spellcheck.test.jsx +0 -63
|
@@ -1,20 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { fireEvent, render } from '@testing-library/react';
|
|
3
|
-
import { color } from '@pie-lib/render-ui';
|
|
4
3
|
import { Button, MarkButton, RawButton, RawMarkButton } from '../common/toolbar-buttons';
|
|
5
4
|
|
|
6
|
-
/*
|
|
7
|
-
* jsdom does not resolve custom properties, so toHaveStyle() normalises `var(--x, y)`
|
|
8
|
-
* to the empty string and cannot compare it. These buttons now take their ink from
|
|
9
|
-
* `--pie-text`, so assert against the rule emotion emitted for the element's class.
|
|
10
|
-
*/
|
|
11
|
-
const declaredColor = (el) =>
|
|
12
|
-
Array.from(document.querySelectorAll('style'))
|
|
13
|
-
.flatMap((tag) => Array.from(tag.sheet?.cssRules || []))
|
|
14
|
-
.filter((rule) => Array.from(el.classList).some((c) => rule.selectorText?.includes(`.${c}`)))
|
|
15
|
-
.map((rule) => rule.style?.color)
|
|
16
|
-
.find(Boolean);
|
|
17
|
-
|
|
18
5
|
describe('RawButton', () => {
|
|
19
6
|
it('renders without crashing', () => {
|
|
20
7
|
const { container } = render(<RawButton onClick={jest.fn()}>Click me</RawButton>);
|
|
@@ -71,7 +58,7 @@ describe('RawButton', () => {
|
|
|
71
58
|
</RawButton>,
|
|
72
59
|
);
|
|
73
60
|
const button = getByText('Active');
|
|
74
|
-
expect(
|
|
61
|
+
expect(button).toHaveStyle({ color: 'black' });
|
|
75
62
|
});
|
|
76
63
|
|
|
77
64
|
it('applies disabled style when disabled is true', () => {
|
|
@@ -209,7 +196,7 @@ describe('RawMarkButton', () => {
|
|
|
209
196
|
</RawMarkButton>,
|
|
210
197
|
);
|
|
211
198
|
const button = getByText('B');
|
|
212
|
-
expect(
|
|
199
|
+
expect(button).toHaveStyle({ color: 'black' });
|
|
213
200
|
});
|
|
214
201
|
|
|
215
202
|
it('has aria-label from label prop', () => {
|
|
@@ -2,12 +2,11 @@ import React from 'react';
|
|
|
2
2
|
import debug from 'debug';
|
|
3
3
|
import { styled } from '@mui/material/styles';
|
|
4
4
|
import PropTypes from 'prop-types';
|
|
5
|
-
import { color } from '@pie-lib/render-ui';
|
|
6
5
|
|
|
7
6
|
const StyledButton = styled('button', {
|
|
8
7
|
shouldForwardProp: (prop) => !['active', 'disabled', 'extraStyles'].includes(prop),
|
|
9
|
-
})(({ active, disabled }) => ({
|
|
10
|
-
color: active ?
|
|
8
|
+
})(({ theme, active, disabled }) => ({
|
|
9
|
+
color: active ? 'black' : 'grey',
|
|
11
10
|
display: 'inline-flex',
|
|
12
11
|
padding: '2px',
|
|
13
12
|
background: 'none',
|
|
@@ -19,11 +18,10 @@ const StyledButton = styled('button', {
|
|
|
19
18
|
height: '24px',
|
|
20
19
|
},
|
|
21
20
|
'&:hover': {
|
|
22
|
-
color: disabled ?
|
|
21
|
+
color: disabled ? 'grey' : 'black',
|
|
23
22
|
},
|
|
24
23
|
'&:focus': {
|
|
25
|
-
|
|
26
|
-
outline: `2px solid ${color.buttonFocusOutline()}`,
|
|
24
|
+
outline: `2px solid ${theme.palette.grey[700]}`,
|
|
27
25
|
},
|
|
28
26
|
...(disabled && {
|
|
29
27
|
opacity: 0.7,
|
|
@@ -10,15 +10,7 @@ const StyledPath = styled('path')({
|
|
|
10
10
|
|
|
11
11
|
const SvgIcon = (Component) => {
|
|
12
12
|
return (props) => (
|
|
13
|
-
<svg
|
|
14
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
15
|
-
version="1.1"
|
|
16
|
-
width="24"
|
|
17
|
-
height="24"
|
|
18
|
-
viewBox="0 0 24 24"
|
|
19
|
-
aria-hidden="true"
|
|
20
|
-
focusable="false"
|
|
21
|
-
>
|
|
13
|
+
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="24" height="24" viewBox="0 0 24 24">
|
|
22
14
|
<Component {...props} />
|
|
23
15
|
</svg>
|
|
24
16
|
);
|
|
@@ -4,15 +4,7 @@ import List from '@mui/material/List';
|
|
|
4
4
|
import ListItem from '@mui/material/ListItem';
|
|
5
5
|
|
|
6
6
|
export const AlignLeft = () => (
|
|
7
|
-
<svg
|
|
8
|
-
width="20"
|
|
9
|
-
height="20"
|
|
10
|
-
viewBox="0 0 66 66"
|
|
11
|
-
fill="none"
|
|
12
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
13
|
-
aria-hidden="true"
|
|
14
|
-
focusable="false"
|
|
15
|
-
>
|
|
7
|
+
<svg width="20" height="20" viewBox="0 0 66 66" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
16
8
|
<path
|
|
17
9
|
d="M42.1875 4.75C42.1875 7.38672 39.9902 9.4375 37.5 9.4375H4.6875C2.05078 9.4375 0 7.38672 0 4.75C0 2.25977 2.05078 0.0625 4.6875 0.0625H37.5C39.9902 0.0625 42.1875 2.25977 42.1875 4.75ZM42.1875 42.25C42.1875 44.8867 39.9902 46.9375 37.5 46.9375H4.6875C2.05078 46.9375 0 44.8867 0 42.25C0 39.7598 2.05078 37.5625 4.6875 37.5625H37.5C39.9902 37.5625 42.1875 39.7598 42.1875 42.25ZM0 23.5C0 21.0098 2.05078 18.8125 4.6875 18.8125H60.9375C63.4277 18.8125 65.625 21.0098 65.625 23.5C65.625 26.1367 63.4277 28.1875 60.9375 28.1875H4.6875C2.05078 28.1875 0 26.1367 0 23.5ZM65.625 61C65.625 63.6367 63.4277 65.6875 60.9375 65.6875H4.6875C2.05078 65.6875 0 63.6367 0 61C0 58.5098 2.05078 56.3125 4.6875 56.3125H60.9375C63.4277 56.3125 65.625 58.5098 65.625 61Z"
|
|
18
10
|
fill="currentColor"
|
|
@@ -21,15 +13,7 @@ export const AlignLeft = () => (
|
|
|
21
13
|
);
|
|
22
14
|
|
|
23
15
|
export const AlignRight = () => (
|
|
24
|
-
<svg
|
|
25
|
-
width="20"
|
|
26
|
-
height="20"
|
|
27
|
-
viewBox="0 0 66 66"
|
|
28
|
-
fill="none"
|
|
29
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
30
|
-
aria-hidden="true"
|
|
31
|
-
focusable="false"
|
|
32
|
-
>
|
|
16
|
+
<svg width="20" height="20" viewBox="0 0 66 66" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
33
17
|
<path
|
|
34
18
|
d="M65.625 4.75C65.625 7.38672 63.4277 9.4375 60.9375 9.4375H28.125C25.4883 9.4375 23.4375 7.38672 23.4375 4.75C23.4375 2.25977 25.4883 0.0625 28.125 0.0625H60.9375C63.4277 0.0625 65.625 2.25977 65.625 4.75ZM65.625 42.25C65.625 44.8867 63.4277 46.9375 60.9375 46.9375H28.125C25.4883 46.9375 23.4375 44.8867 23.4375 42.25C23.4375 39.7598 25.4883 37.5625 28.125 37.5625H60.9375C63.4277 37.5625 65.625 39.7598 65.625 42.25ZM0 23.5C0 21.0098 2.05078 18.8125 4.6875 18.8125H60.9375C63.4277 18.8125 65.625 21.0098 65.625 23.5C65.625 26.1367 63.4277 28.1875 60.9375 28.1875H4.6875C2.05078 28.1875 0 26.1367 0 23.5ZM65.625 61C65.625 63.6367 63.4277 65.6875 60.9375 65.6875H4.6875C2.05078 65.6875 0 63.6367 0 61C0 58.5098 2.05078 56.3125 4.6875 56.3125H60.9375C63.4277 56.3125 65.625 58.5098 65.625 61Z"
|
|
35
19
|
fill="currentColor"
|
|
@@ -38,15 +22,7 @@ export const AlignRight = () => (
|
|
|
38
22
|
);
|
|
39
23
|
|
|
40
24
|
export const AlignCenter = () => (
|
|
41
|
-
<svg
|
|
42
|
-
width="20"
|
|
43
|
-
height="20"
|
|
44
|
-
viewBox="0 0 66 66"
|
|
45
|
-
fill="none"
|
|
46
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
47
|
-
aria-hidden="true"
|
|
48
|
-
focusable="false"
|
|
49
|
-
>
|
|
25
|
+
<svg width="20" height="20" viewBox="0 0 66 66" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
50
26
|
<path
|
|
51
27
|
d="M51.5625 4.75C51.5625 7.38672 49.3652 9.4375 46.875 9.4375H18.75C16.1133 9.4375 14.0625 7.38672 14.0625 4.75C14.0625 2.25977 16.1133 0.0625 18.75 0.0625H46.875C49.3652 0.0625 51.5625 2.25977 51.5625 4.75ZM65.625 23.5C65.625 26.1367 63.4277 28.1875 60.9375 28.1875H4.6875C2.05078 28.1875 0 26.1367 0 23.5C0 21.0098 2.05078 18.8125 4.6875 18.8125H60.9375C63.4277 18.8125 65.625 21.0098 65.625 23.5ZM0 61C0 58.5098 2.05078 56.3125 4.6875 56.3125H60.9375C63.4277 56.3125 65.625 58.5098 65.625 61C65.625 63.6367 63.4277 65.6875 60.9375 65.6875H4.6875C2.05078 65.6875 0 63.6367 0 61ZM51.5625 42.25C51.5625 44.8867 49.3652 46.9375 46.875 46.9375H18.75C16.1133 46.9375 14.0625 44.8867 14.0625 42.25C14.0625 39.7598 16.1133 37.5625 18.75 37.5625H46.875C49.3652 37.5625 51.5625 39.7598 51.5625 42.25Z"
|
|
52
28
|
fill="currentColor"
|
|
@@ -55,15 +31,7 @@ export const AlignCenter = () => (
|
|
|
55
31
|
);
|
|
56
32
|
|
|
57
33
|
export const AlignJustify = () => (
|
|
58
|
-
<svg
|
|
59
|
-
width="20"
|
|
60
|
-
height="20"
|
|
61
|
-
viewBox="0 0 66 66"
|
|
62
|
-
fill="none"
|
|
63
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
64
|
-
aria-hidden="true"
|
|
65
|
-
focusable="false"
|
|
66
|
-
>
|
|
34
|
+
<svg width="20" height="20" viewBox="0 0 66 66" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
67
35
|
<path
|
|
68
36
|
d="M65.625 4.75C65.625 7.38672 63.4277 9.4375 60.9375 9.4375H4.6875C2.05078 9.4375 0 7.38672 0 4.75C0 2.25977 2.05078 0.0625 4.6875 0.0625H60.9375C63.4277 0.0625 65.625 2.25977 65.625 4.75ZM65.625 42.25C65.625 44.8867 63.4277 46.9375 60.9375 46.9375H4.6875C2.05078 46.9375 0 44.8867 0 42.25C0 39.7598 2.05078 37.5625 4.6875 37.5625H60.9375C63.4277 37.5625 65.625 39.7598 65.625 42.25ZM0 23.5C0 21.0098 2.05078 18.8125 4.6875 18.8125H60.9375C63.4277 18.8125 65.625 21.0098 65.625 23.5C65.625 26.1367 63.4277 28.1875 60.9375 28.1875H4.6875C2.05078 28.1875 0 26.1367 0 23.5ZM65.625 61C65.625 63.6367 63.4277 65.6875 60.9375 65.6875H4.6875C2.05078 65.6875 0 63.6367 0 61C0 58.5098 2.05078 56.3125 4.6875 56.3125H60.9375C63.4277 56.3125 65.625 58.5098 65.625 61Z"
|
|
69
37
|
fill="currentColor"
|
|
@@ -111,9 +79,7 @@ export default ({ editor, onChange }) => {
|
|
|
111
79
|
<div>
|
|
112
80
|
<div style={{ display: 'flex', alignItems: 'center' }} onClick={onMouseDown}>
|
|
113
81
|
{icon}
|
|
114
|
-
<span style={{ marginLeft: '5px', fontSize: '8px' }}
|
|
115
|
-
▼
|
|
116
|
-
</span>
|
|
82
|
+
<span style={{ marginLeft: '5px', fontSize: '8px' }}>▼</span>
|
|
117
83
|
</div>
|
|
118
84
|
<Collapse in={open} timeout="auto" unmountOnExit style={{ position: 'absolute' }}>
|
|
119
85
|
<List
|
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { NodeViewWrapper } from '@tiptap/react';
|
|
4
|
-
import { mq } from '@pie-lib/math-input';
|
|
4
|
+
import { mq } from '@pie-lib/math-input-mathlive';
|
|
5
5
|
import { styled } from '@mui/material/styles';
|
|
6
|
-
import { color } from '@pie-lib/render-ui';
|
|
7
6
|
|
|
8
7
|
const StyledSpanContainer = styled('span')(() => ({
|
|
9
8
|
display: 'inline-flex',
|
|
10
|
-
|
|
11
|
-
* #C0C3CF is `--pie-blue-grey-300`'s default value, but that token is a fill: it
|
|
12
|
-
* measures 1.00:1 against black-on-rose's own background. The box outline is what
|
|
13
|
-
* marks the response slot in running text, so it takes the stroke token instead.
|
|
14
|
-
*/
|
|
15
|
-
border: `1px solid ${color.border()}`,
|
|
9
|
+
border: '1px solid #C0C3CF',
|
|
16
10
|
margin: '1px 5px',
|
|
17
11
|
cursor: 'pointer',
|
|
18
12
|
alignItems: 'center',
|
|
@@ -22,12 +16,11 @@ const StyledSpanContainer = styled('span')(() => ({
|
|
|
22
16
|
height: 'fit-content',
|
|
23
17
|
}));
|
|
24
18
|
|
|
25
|
-
const StyledResponseBox = styled('div')(() => ({
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
color: color.text(),
|
|
19
|
+
const StyledResponseBox = styled('div')(({ theme }) => ({
|
|
20
|
+
background: theme.palette.grey['A100'],
|
|
21
|
+
color: theme.palette.grey['A700'],
|
|
29
22
|
display: 'inline-flex',
|
|
30
|
-
borderRight:
|
|
23
|
+
borderRight: '2px solid #C0C3CF',
|
|
31
24
|
boxSizing: 'border-box',
|
|
32
25
|
overflow: 'hidden',
|
|
33
26
|
fontSize: '12px',
|
|
@@ -765,11 +765,13 @@ describe('MathNodeView', () => {
|
|
|
765
765
|
dateNowSpy.mockRestore();
|
|
766
766
|
});
|
|
767
767
|
|
|
768
|
+
// The wrapper is an emotion styled() component, so className also carries
|
|
769
|
+
// emotion's generated class - assert on the math-node token, not equality.
|
|
768
770
|
it('assigns a timestamp-based class to NodeViewWrapper', () => {
|
|
769
771
|
const { getByTestId } = render(<MathNodeView {...defaultProps} />);
|
|
770
772
|
const wrapper = getByTestId('node-view-wrapper');
|
|
771
773
|
|
|
772
|
-
expect(wrapper.
|
|
774
|
+
expect(Array.from(wrapper.classList).some((c) => /^math-node-\d+$/.test(c))).toBe(true);
|
|
773
775
|
});
|
|
774
776
|
|
|
775
777
|
it('assigns a different class to each MathNodeView instance', () => {
|
|
@@ -781,8 +783,12 @@ describe('MathNodeView', () => {
|
|
|
781
783
|
);
|
|
782
784
|
|
|
783
785
|
const wrappers = getAllByTestId('node-view-wrapper');
|
|
784
|
-
|
|
785
|
-
|
|
786
|
+
const mathNodeClass = (el) => Array.from(el.classList).find((c) => c.startsWith('math-node-'));
|
|
787
|
+
|
|
788
|
+
expect(mathNodeClass(wrappers[0])).toBe('math-node-1000');
|
|
789
|
+
expect(mathNodeClass(wrappers[1])).toBe('math-node-1001');
|
|
790
|
+
// and they must differ
|
|
791
|
+
expect(mathNodeClass(wrappers[0])).not.toBe(mathNodeClass(wrappers[1]));
|
|
786
792
|
});
|
|
787
793
|
|
|
788
794
|
it('closes toolbar when clicking a different math node', async () => {
|
|
@@ -835,9 +841,7 @@ describe('MathNodeView', () => {
|
|
|
835
841
|
},
|
|
836
842
|
};
|
|
837
843
|
|
|
838
|
-
const { queryByTestId, rerender } = render(
|
|
839
|
-
<MathNodeView {...defaultProps} editor={editor} selected={false} />,
|
|
840
|
-
);
|
|
844
|
+
const { queryByTestId, rerender } = render(<MathNodeView {...defaultProps} editor={editor} selected={false} />);
|
|
841
845
|
rerender(<MathNodeView {...defaultProps} editor={editor} selected={true} />);
|
|
842
846
|
|
|
843
847
|
await act(async () => {});
|
|
@@ -853,9 +857,7 @@ describe('MathNodeView', () => {
|
|
|
853
857
|
},
|
|
854
858
|
};
|
|
855
859
|
|
|
856
|
-
const { queryByTestId, rerender } = render(
|
|
857
|
-
<MathNodeView {...defaultProps} editor={editor} selected={false} />,
|
|
858
|
-
);
|
|
860
|
+
const { queryByTestId, rerender } = render(<MathNodeView {...defaultProps} editor={editor} selected={false} />);
|
|
859
861
|
rerender(<MathNodeView {...defaultProps} editor={editor} selected={true} />);
|
|
860
862
|
|
|
861
863
|
await act(async () => {});
|
package/src/extensions/math.js
CHANGED
|
@@ -3,6 +3,7 @@ import ReactDOM from 'react-dom';
|
|
|
3
3
|
import { Node } from '@tiptap/core';
|
|
4
4
|
import { NodeViewWrapper, ReactNodeViewRenderer } from '@tiptap/react';
|
|
5
5
|
import { NodeSelection, Plugin, PluginKey, TextSelection } from 'prosemirror-state';
|
|
6
|
+
import { styled } from '@mui/material/styles';
|
|
6
7
|
import { MathPreview, MathToolbar } from '@pie-lib/math-toolbar';
|
|
7
8
|
import { wrapMath } from '@pie-lib/math-rendering';
|
|
8
9
|
import { setToolbarOpened } from '../utils/toolbar';
|
|
@@ -116,6 +117,21 @@ export const ZeroWidthSpaceHandlingPlugin = new Plugin({
|
|
|
116
117
|
},
|
|
117
118
|
});
|
|
118
119
|
|
|
120
|
+
const StyledMathNodeWrapper = styled(NodeViewWrapper, {
|
|
121
|
+
shouldForwardProp: (prop) => prop !== 'selected',
|
|
122
|
+
})(({ theme, selected }) => ({
|
|
123
|
+
display: 'inline-flex',
|
|
124
|
+
cursor: 'pointer',
|
|
125
|
+
margin: '0 4px',
|
|
126
|
+
'&:not(:has(.ML__latex))': {
|
|
127
|
+
border: 'solid 1px lightgrey',
|
|
128
|
+
padding: '5px',
|
|
129
|
+
},
|
|
130
|
+
...(selected && {
|
|
131
|
+
border: `solid 1px ${theme.palette.primary.main}`,
|
|
132
|
+
}),
|
|
133
|
+
}));
|
|
134
|
+
|
|
119
135
|
export const MathNode = Node.create({
|
|
120
136
|
name: 'math',
|
|
121
137
|
group: 'inline',
|
|
@@ -400,17 +416,9 @@ export const MathNodeView = (props) => {
|
|
|
400
416
|
}, [editor, showToolbar, node]);
|
|
401
417
|
|
|
402
418
|
return (
|
|
403
|
-
<
|
|
404
|
-
className={`math-node-${timestamp.current}`}
|
|
405
|
-
style={{
|
|
406
|
-
display: 'inline-flex',
|
|
407
|
-
cursor: 'pointer',
|
|
408
|
-
margin: '0 4px',
|
|
409
|
-
}}
|
|
410
|
-
data-selected={selected}
|
|
411
|
-
>
|
|
419
|
+
<StyledMathNodeWrapper className={`math-node-${timestamp.current}`} selected={selected} data-selected={selected}>
|
|
412
420
|
<div ref={nodeRef} onClick={() => setShowToolbar(true)} contentEditable={false}>
|
|
413
|
-
<MathPreview latex={latex} />
|
|
421
|
+
<MathPreview latex={latex} isSelected={selected} outlineOnWrapper />
|
|
414
422
|
</div>
|
|
415
423
|
{showToolbar &&
|
|
416
424
|
ReactDOM.createPortal(
|
|
@@ -441,6 +449,6 @@ export const MathNodeView = (props) => {
|
|
|
441
449
|
</div>,
|
|
442
450
|
editor?._tiptapContainerEl || document.body,
|
|
443
451
|
)}
|
|
444
|
-
</
|
|
452
|
+
</StyledMathNodeWrapper>
|
|
445
453
|
);
|
|
446
454
|
};
|
|
@@ -101,14 +101,14 @@ const styles = (theme) => ({
|
|
|
101
101
|
backgroundColor: color.background(),
|
|
102
102
|
},
|
|
103
103
|
'& table:not([border="1"]) tr': {
|
|
104
|
-
borderTop:
|
|
104
|
+
borderTop: '1px solid #dfe2e5',
|
|
105
105
|
},
|
|
106
106
|
'& td, th': {
|
|
107
107
|
padding: '.6em 1em',
|
|
108
108
|
textAlign: 'center',
|
|
109
109
|
},
|
|
110
110
|
'& table:not([border="1"]) td, th': {
|
|
111
|
-
border:
|
|
111
|
+
border: '1px solid #dfe2e5',
|
|
112
112
|
},
|
|
113
113
|
},
|
|
114
114
|
children: {
|
package/LICENSE.md
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
Copyright 2019 CoreSpring Inc
|
|
2
|
-
|
|
3
|
-
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
4
|
-
|
|
5
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { render, waitFor } from '@testing-library/react';
|
|
3
|
-
import { EditableHtml } from '../components/EditableHtml';
|
|
4
|
-
|
|
5
|
-
const renderEditor = (props) =>
|
|
6
|
-
render(<EditableHtml markup="<p>hello</p>" onChange={() => {}} pluginProps={{}} {...props} />);
|
|
7
|
-
|
|
8
|
-
const prosemirror = (container) => container.querySelector('.ProseMirror');
|
|
9
|
-
|
|
10
|
-
const waitForEditor = async (container) => {
|
|
11
|
-
await waitFor(() => {
|
|
12
|
-
expect(prosemirror(container)).toBeInTheDocument();
|
|
13
|
-
});
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
describe('contenteditable attributes', () => {
|
|
17
|
-
// PIE-1015: 2.1.17 lost role="textbox" on a plain mount, because setEditable re-pushes
|
|
18
|
-
// editorProps through setOptions and ProseMirror drops any attribute no longer listed.
|
|
19
|
-
it('announces the editor as a textbox on a plain mount', async () => {
|
|
20
|
-
const { container } = renderEditor();
|
|
21
|
-
|
|
22
|
-
await waitForEditor(container);
|
|
23
|
-
|
|
24
|
-
expect(prosemirror(container).getAttribute('role')).toBe('textbox');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
// PIE-1016: a PIE-owned hook external consumers can target, unlike role or .tiptap.ProseMirror.
|
|
28
|
-
it('exposes the PIE-owned editor hook on a plain mount', async () => {
|
|
29
|
-
const { container } = renderEditor();
|
|
30
|
-
|
|
31
|
-
await waitForEditor(container);
|
|
32
|
-
|
|
33
|
-
expect(prosemirror(container).getAttribute('data-pie-editor')).toBe('true');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('keeps both attributes when spellCheck changes on a mounted editor', async () => {
|
|
37
|
-
const { container, rerender } = renderEditor({ spellCheck: true });
|
|
38
|
-
|
|
39
|
-
await waitForEditor(container);
|
|
40
|
-
|
|
41
|
-
rerender(<EditableHtml markup="<p>hello</p>" onChange={() => {}} pluginProps={{}} spellCheck={false} />);
|
|
42
|
-
|
|
43
|
-
await waitFor(() => {
|
|
44
|
-
expect(prosemirror(container).getAttribute('spellcheck')).toBe('false');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
expect(prosemirror(container).getAttribute('role')).toBe('textbox');
|
|
48
|
-
expect(prosemirror(container).getAttribute('data-pie-editor')).toBe('true');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('keeps both attributes when the editor is disabled on a mounted editor', async () => {
|
|
52
|
-
const { container, rerender } = renderEditor();
|
|
53
|
-
|
|
54
|
-
await waitForEditor(container);
|
|
55
|
-
|
|
56
|
-
rerender(<EditableHtml markup="<p>hello</p>" onChange={() => {}} pluginProps={{}} disabled />);
|
|
57
|
-
|
|
58
|
-
await waitFor(() => {
|
|
59
|
-
expect(prosemirror(container).getAttribute('contenteditable')).toBe('false');
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
expect(prosemirror(container).getAttribute('role')).toBe('textbox');
|
|
63
|
-
expect(prosemirror(container).getAttribute('data-pie-editor')).toBe('true');
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// Online Testing queries [data-pie-editor][contenteditable="true"] to attach its answer-save
|
|
67
|
-
// listener, so a read-only render has to carry the hook but not match that selector.
|
|
68
|
-
it('marks a read-only editor as not editable so the hook can be filtered on it', async () => {
|
|
69
|
-
const { container } = renderEditor({ disabled: true });
|
|
70
|
-
|
|
71
|
-
await waitForEditor(container);
|
|
72
|
-
|
|
73
|
-
expect(prosemirror(container).getAttribute('data-pie-editor')).toBe('true');
|
|
74
|
-
expect(prosemirror(container).getAttribute('contenteditable')).toBe('false');
|
|
75
|
-
expect(container.querySelector('[data-pie-editor][contenteditable="true"]')).toBeNull();
|
|
76
|
-
});
|
|
77
|
-
});
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { render, waitFor } from '@testing-library/react';
|
|
3
|
-
import { EditableHtml } from '../components/EditableHtml';
|
|
4
|
-
|
|
5
|
-
const renderEditor = (props) =>
|
|
6
|
-
render(<EditableHtml markup="<p>teh</p>" onChange={() => {}} pluginProps={{}} {...props} />);
|
|
7
|
-
|
|
8
|
-
const prosemirror = (container) => container.querySelector('.ProseMirror');
|
|
9
|
-
|
|
10
|
-
const waitForEditor = async (container) => {
|
|
11
|
-
await waitFor(() => {
|
|
12
|
-
expect(prosemirror(container)).toBeInTheDocument();
|
|
13
|
-
});
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
describe('spellCheck', () => {
|
|
17
|
-
it('turns off browser spellcheck on the contenteditable when spellCheck is false', async () => {
|
|
18
|
-
const { container } = renderEditor({ spellCheck: false });
|
|
19
|
-
|
|
20
|
-
await waitForEditor(container);
|
|
21
|
-
|
|
22
|
-
expect(prosemirror(container).getAttribute('spellcheck')).toBe('false');
|
|
23
|
-
expect(prosemirror(container).getAttribute('autocorrect')).toBe('off');
|
|
24
|
-
expect(prosemirror(container).getAttribute('autocapitalize')).toBe('off');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('leaves browser spellcheck on when spellCheck is true', async () => {
|
|
28
|
-
const { container } = renderEditor({ spellCheck: true });
|
|
29
|
-
|
|
30
|
-
await waitForEditor(container);
|
|
31
|
-
|
|
32
|
-
expect(prosemirror(container).getAttribute('spellcheck')).toBe('true');
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('keeps the browser default when spellCheck is not provided', async () => {
|
|
36
|
-
const { container } = renderEditor();
|
|
37
|
-
|
|
38
|
-
await waitForEditor(container);
|
|
39
|
-
|
|
40
|
-
expect(prosemirror(container).getAttribute('spellcheck')).toBe('true');
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('updates the contenteditable when spellCheck changes on an existing editor', async () => {
|
|
44
|
-
const { container, rerender } = renderEditor({ spellCheck: true });
|
|
45
|
-
|
|
46
|
-
await waitForEditor(container);
|
|
47
|
-
expect(prosemirror(container).getAttribute('spellcheck')).toBe('true');
|
|
48
|
-
|
|
49
|
-
rerender(<EditableHtml markup="<p>teh</p>" onChange={() => {}} pluginProps={{}} spellCheck={false} />);
|
|
50
|
-
|
|
51
|
-
await waitFor(() => {
|
|
52
|
-
expect(prosemirror(container).getAttribute('spellcheck')).toBe('false');
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('keeps spellcheck off for a disabled (read-only) editor', async () => {
|
|
57
|
-
const { container } = renderEditor({ spellCheck: false, disabled: true });
|
|
58
|
-
|
|
59
|
-
await waitForEditor(container);
|
|
60
|
-
|
|
61
|
-
expect(prosemirror(container).getAttribute('spellcheck')).toBe('false');
|
|
62
|
-
});
|
|
63
|
-
});
|