@pie-lib/editable-html-tip-tap 1.2.0-next.0 → 1.2.0-next.12

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 (39) hide show
  1. package/CHANGELOG.md +61 -22
  2. package/lib/components/CharacterPicker.js +4 -4
  3. package/lib/components/CharacterPicker.js.map +1 -1
  4. package/lib/components/EditableHtml.js +22 -5
  5. package/lib/components/EditableHtml.js.map +1 -1
  6. package/lib/components/MenuBar.js +30 -12
  7. package/lib/components/MenuBar.js.map +1 -1
  8. package/lib/components/TiptapContainer.js +10 -5
  9. package/lib/components/TiptapContainer.js.map +1 -1
  10. package/lib/components/respArea/DragInTheBlank/DragInTheBlank.js +8 -3
  11. package/lib/components/respArea/DragInTheBlank/DragInTheBlank.js.map +1 -1
  12. package/lib/components/respArea/DragInTheBlank/choice.js +13 -6
  13. package/lib/components/respArea/DragInTheBlank/choice.js.map +1 -1
  14. package/lib/components/respArea/MathTemplated.js +130 -0
  15. package/lib/components/respArea/MathTemplated.js.map +1 -0
  16. package/lib/extensions/image-component.js +4 -4
  17. package/lib/extensions/image-component.js.map +1 -1
  18. package/lib/extensions/index.js +7 -4
  19. package/lib/extensions/index.js.map +1 -1
  20. package/lib/extensions/media.js +14 -11
  21. package/lib/extensions/media.js.map +1 -1
  22. package/lib/extensions/responseArea.js +11 -8
  23. package/lib/extensions/responseArea.js.map +1 -1
  24. package/package.json +12 -14
  25. package/src/__tests__/EditableHtml.test.jsx +3 -0
  26. package/src/components/CharacterPicker.jsx +1 -1
  27. package/src/components/EditableHtml.jsx +28 -5
  28. package/src/components/MenuBar.jsx +37 -6
  29. package/src/components/TiptapContainer.jsx +6 -4
  30. package/src/components/respArea/DragInTheBlank/DragInTheBlank.jsx +7 -2
  31. package/src/components/respArea/DragInTheBlank/choice.jsx +31 -4
  32. package/src/components/respArea/MathTemplated.jsx +124 -0
  33. package/src/components/respArea/__tests__/MathTemplated.test.jsx +210 -0
  34. package/src/extensions/__tests__/media-node-view.test.jsx +296 -0
  35. package/src/extensions/__tests__/media.test.js +1 -0
  36. package/src/extensions/image-component.jsx +1 -1
  37. package/src/extensions/index.js +4 -1
  38. package/src/extensions/media.js +17 -14
  39. package/src/extensions/responseArea.js +3 -7
@@ -0,0 +1,124 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { NodeViewWrapper } from '@tiptap/react';
4
+ import { mq } from '@pie-lib/math-input';
5
+ import { styled } from '@mui/material/styles';
6
+
7
+ const StyledSpanContainer = styled('span')(() => ({
8
+ display: 'inline-flex',
9
+ border: '1px solid #C0C3CF',
10
+ margin: '1px 5px',
11
+ cursor: 'pointer',
12
+ alignItems: 'center',
13
+ justifyContent: 'center',
14
+ minWidth: '50px',
15
+ minHeight: '36px',
16
+ height: 'fit-content',
17
+ }));
18
+
19
+ const StyledResponseBox = styled('div')(({ theme }) => ({
20
+ background: theme.palette.grey['A100'],
21
+ color: theme.palette.grey['A700'],
22
+ display: 'inline-flex',
23
+ borderRight: '2px solid #C0C3CF',
24
+ boxSizing: 'border-box',
25
+ overflow: 'hidden',
26
+ fontSize: '12px',
27
+ minHeight: '36px',
28
+ height: '100%',
29
+ alignItems: 'center',
30
+ fontFamily: 'Symbola, Times New Roman, serif',
31
+ padding: '0 2px',
32
+ }));
33
+
34
+ const StyledMathBlock = styled('div')(() => ({
35
+ flex: 8,
36
+ color: 'var(--pie-text, black)',
37
+ padding: '4px !important',
38
+ display: 'flex',
39
+ alignItems: 'center',
40
+ justifyContent: 'center',
41
+ backgroundColor: 'var(--pie-background, rgba(255, 255, 255, 0))',
42
+ '& > .mq-math-mode sup.mq-nthroot': {
43
+ fontSize: '70% !important',
44
+ verticalAlign: '1em !important',
45
+ },
46
+ '& > .mq-math-mode .mq-sqrt-stem': {
47
+ borderTop: '0.07em solid',
48
+ marginLeft: '-1.5px',
49
+ marginTop: '-2px !important',
50
+ paddingTop: '5px !important',
51
+ },
52
+ '& .mq-overarrow-inner': {
53
+ paddingTop: '0 !important',
54
+ border: 'none !important',
55
+ },
56
+ '& .mq-overarrow.mq-arrow-both': {
57
+ marginTop: '0px',
58
+ minWidth: '1.23em',
59
+ '& *': {
60
+ lineHeight: '1 !important',
61
+ },
62
+ '&:before': {
63
+ top: '-0.4em',
64
+ left: '-1px',
65
+ },
66
+ '&:after': {
67
+ top: '0px !important',
68
+ position: 'absolute !important',
69
+ right: '-2px',
70
+ },
71
+ '&.mq-empty:after': {
72
+ top: '-0.45em',
73
+ },
74
+ },
75
+ '& .mq-overarrow.mq-arrow-right': {
76
+ '&:before': {
77
+ top: '-0.4em',
78
+ right: '-1px',
79
+ },
80
+ },
81
+ '& .mq-overarrow-inner-right': {
82
+ display: 'none !important',
83
+ },
84
+ '& .mq-overarrow-inner-left': {
85
+ display: 'none !important',
86
+ },
87
+ }));
88
+ const MathTemplated = (props) => {
89
+ const { node, options, selected } = props;
90
+ const { attrs: attributes } = node;
91
+ const { value, index } = attributes;
92
+
93
+ // add 1 to index to display R 1 instead of R 0
94
+ const keyToDisplay = `R ${parseInt(index) + 1}`;
95
+
96
+ // console.log({nodeProps.children})
97
+ return (
98
+ <NodeViewWrapper
99
+ className="math-templated"
100
+ data-selected={selected}
101
+ style={{
102
+ display: 'inline-flex',
103
+ minHeight: '36px',
104
+ minWidth: '50px',
105
+ cursor: 'pointer',
106
+ }}
107
+ >
108
+ <StyledSpanContainer {...attributes}>
109
+ <StyledResponseBox>{keyToDisplay}</StyledResponseBox>
110
+ <StyledMathBlock>
111
+ <mq.Static latex={value} />
112
+ </StyledMathBlock>
113
+ </StyledSpanContainer>
114
+ </NodeViewWrapper>
115
+ );
116
+ };
117
+
118
+ MathTemplated.propTypes = {
119
+ attributes: PropTypes.object,
120
+ value: PropTypes.string,
121
+ keyToDisplay: PropTypes.string,
122
+ };
123
+
124
+ export default MathTemplated;
@@ -0,0 +1,210 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react';
3
+ import MathTemplated from '../MathTemplated';
4
+
5
+ // Mock the dependencies
6
+ jest.mock('@tiptap/react', () => ({
7
+ NodeViewWrapper: ({ children, ...props }) => (
8
+ <div data-testid="node-view-wrapper" {...props}>
9
+ {children}
10
+ </div>
11
+ ),
12
+ }));
13
+
14
+ jest.mock('@pie-lib/math-input', () => ({
15
+ mq: {
16
+ Static: ({ latex }) => (
17
+ <div data-testid="mq-static" data-latex={latex}>
18
+ {latex}
19
+ </div>
20
+ ),
21
+ },
22
+ }));
23
+
24
+ describe('MathTemplated', () => {
25
+ const defaultProps = {
26
+ node: {
27
+ attrs: {
28
+ value: 'x^2 + y^2 = r^2',
29
+ index: 0,
30
+ },
31
+ },
32
+ options: {},
33
+ selected: false,
34
+ };
35
+
36
+ it('renders without crashing', () => {
37
+ const { container } = render(<MathTemplated {...defaultProps} />);
38
+ expect(container).toBeInTheDocument();
39
+ });
40
+
41
+ it('renders NodeViewWrapper with correct className', () => {
42
+ const { getByTestId } = render(<MathTemplated {...defaultProps} />);
43
+ const wrapper = getByTestId('node-view-wrapper');
44
+ expect(wrapper).toHaveClass('math-templated');
45
+ });
46
+
47
+ it('displays correct response key for index 0', () => {
48
+ const { getByText } = render(<MathTemplated {...defaultProps} />);
49
+ expect(getByText('R 1')).toBeInTheDocument();
50
+ });
51
+
52
+ it('displays correct response key for index 1', () => {
53
+ const props = {
54
+ ...defaultProps,
55
+ node: {
56
+ attrs: {
57
+ value: 'a + b',
58
+ index: 1,
59
+ },
60
+ },
61
+ };
62
+ const { getByText } = render(<MathTemplated {...props} />);
63
+ expect(getByText('R 2')).toBeInTheDocument();
64
+ });
65
+
66
+ it('displays correct response key for index 5', () => {
67
+ const props = {
68
+ ...defaultProps,
69
+ node: {
70
+ attrs: {
71
+ value: 'c = d',
72
+ index: 5,
73
+ },
74
+ },
75
+ };
76
+ const { getByText } = render(<MathTemplated {...props} />);
77
+ expect(getByText('R 6')).toBeInTheDocument();
78
+ });
79
+
80
+ it('renders LaTeX value correctly', () => {
81
+ const { getByTestId } = render(<MathTemplated {...defaultProps} />);
82
+ const mqStatic = getByTestId('mq-static');
83
+ expect(mqStatic).toHaveAttribute('data-latex', 'x^2 + y^2 = r^2');
84
+ });
85
+
86
+ it('renders different LaTeX value', () => {
87
+ const props = {
88
+ ...defaultProps,
89
+ node: {
90
+ attrs: {
91
+ value: '\\frac{a}{b}',
92
+ index: 2,
93
+ },
94
+ },
95
+ };
96
+ const { getByTestId } = render(<MathTemplated {...props} />);
97
+ const mqStatic = getByTestId('mq-static');
98
+ expect(mqStatic).toHaveAttribute('data-latex', '\\frac{a}{b}');
99
+ });
100
+
101
+ it('passes selected prop to NodeViewWrapper', () => {
102
+ const props = {
103
+ ...defaultProps,
104
+ selected: true,
105
+ };
106
+ const { getByTestId } = render(<MathTemplated {...props} />);
107
+ const wrapper = getByTestId('node-view-wrapper');
108
+ expect(wrapper).toHaveAttribute('data-selected', 'true');
109
+ });
110
+
111
+ it('passes false selected prop to NodeViewWrapper', () => {
112
+ const { getByTestId } = render(<MathTemplated {...defaultProps} />);
113
+ const wrapper = getByTestId('node-view-wrapper');
114
+ expect(wrapper).toHaveAttribute('data-selected', 'false');
115
+ });
116
+
117
+ it('applies correct inline styles to NodeViewWrapper', () => {
118
+ const { getByTestId } = render(<MathTemplated {...defaultProps} />);
119
+ const wrapper = getByTestId('node-view-wrapper');
120
+ expect(wrapper).toHaveStyle({
121
+ display: 'inline-flex',
122
+ minHeight: '36px',
123
+ minWidth: '50px',
124
+ cursor: 'pointer',
125
+ });
126
+ });
127
+
128
+ it('handles string index correctly', () => {
129
+ const props = {
130
+ ...defaultProps,
131
+ node: {
132
+ attrs: {
133
+ value: 'test',
134
+ index: '3',
135
+ },
136
+ },
137
+ };
138
+ const { getByText } = render(<MathTemplated {...props} />);
139
+ expect(getByText('R 4')).toBeInTheDocument();
140
+ });
141
+
142
+ it('handles empty value', () => {
143
+ const props = {
144
+ ...defaultProps,
145
+ node: {
146
+ attrs: {
147
+ value: '',
148
+ index: 0,
149
+ },
150
+ },
151
+ };
152
+ const { getByTestId } = render(<MathTemplated {...props} />);
153
+ const mqStatic = getByTestId('mq-static');
154
+ expect(mqStatic).toHaveAttribute('data-latex', '');
155
+ });
156
+
157
+ it('renders all styled components', () => {
158
+ const { container, getByText } = render(<MathTemplated {...defaultProps} />);
159
+
160
+ // Check for response box
161
+ expect(getByText('R 1')).toBeInTheDocument();
162
+
163
+ // Check for math block with LaTeX
164
+ const mqStatic = container.querySelector('[data-testid="mq-static"]');
165
+ expect(mqStatic).toBeInTheDocument();
166
+ });
167
+
168
+ it('handles zero index', () => {
169
+ const props = {
170
+ ...defaultProps,
171
+ node: {
172
+ attrs: {
173
+ value: 'x = 0',
174
+ index: 0,
175
+ },
176
+ },
177
+ };
178
+ const { getByText } = render(<MathTemplated {...props} />);
179
+ expect(getByText('R 1')).toBeInTheDocument();
180
+ });
181
+
182
+ it('handles large index values', () => {
183
+ const props = {
184
+ ...defaultProps,
185
+ node: {
186
+ attrs: {
187
+ value: 'x = 100',
188
+ index: 99,
189
+ },
190
+ },
191
+ };
192
+ const { getByText } = render(<MathTemplated {...props} />);
193
+ expect(getByText('R 100')).toBeInTheDocument();
194
+ });
195
+
196
+ it('renders with complex LaTeX expression', () => {
197
+ const props = {
198
+ ...defaultProps,
199
+ node: {
200
+ attrs: {
201
+ value: '\\sqrt{x^2 + y^2}',
202
+ index: 0,
203
+ },
204
+ },
205
+ };
206
+ const { getByTestId } = render(<MathTemplated {...props} />);
207
+ const mqStatic = getByTestId('mq-static');
208
+ expect(mqStatic).toHaveAttribute('data-latex', '\\sqrt{x^2 + y^2}');
209
+ });
210
+ });
@@ -0,0 +1,296 @@
1
+ // Create a mockable insertDialog function
2
+ const mockInsertDialog = jest.fn();
3
+
4
+ // Mock react-dom BEFORE any imports that depend on it
5
+ jest.mock('react-dom', () => ({
6
+ ...jest.requireActual('react-dom'),
7
+ render: jest.fn(),
8
+ }));
9
+
10
+ // Mock the insertDialog function in the media module
11
+ jest.mock('../media', () => {
12
+ const actual = jest.requireActual('../media');
13
+ return {
14
+ __esModule: true,
15
+ ...actual,
16
+ default: actual.default, // Preserve the default export
17
+ insertDialog: (...args) => mockInsertDialog(...args),
18
+ };
19
+ });
20
+
21
+ import React from 'react';
22
+ import { render, fireEvent } from '@testing-library/react';
23
+ import MediaNodeView from '../media';
24
+
25
+ jest.mock('@tiptap/core', () => ({
26
+ Node: { create: jest.fn((config) => config) },
27
+ mergeAttributes: jest.fn((...args) => Object.assign({}, ...args)),
28
+ }));
29
+
30
+ jest.mock('@tiptap/react', () => ({
31
+ NodeViewWrapper: ({ children }) => <div data-testid="node-view-wrapper">{children}</div>,
32
+ ReactNodeViewRenderer: jest.fn((component) => component),
33
+ }));
34
+
35
+ jest.mock('../../components/media/MediaDialog', () => ({
36
+ __esModule: true,
37
+ default: jest.fn(() => <div data-testid="media-dialog" />),
38
+ }));
39
+
40
+ jest.mock('../../components/media/MediaToolbar', () => ({
41
+ __esModule: true,
42
+ default: jest.fn(({ onEdit, onRemove }) => (
43
+ <div data-testid="media-toolbar">
44
+ <button onClick={onEdit} data-testid="edit-button">
45
+ Edit Settings
46
+ </button>
47
+ <button onClick={onRemove} data-testid="remove-button">
48
+ Remove
49
+ </button>
50
+ </div>
51
+ )),
52
+ }));
53
+
54
+ describe('MediaNodeView Component', () => {
55
+ let mockEditor;
56
+ let mockUpdateAttributes;
57
+ let mockDeleteNode;
58
+
59
+ beforeEach(() => {
60
+ jest.clearAllMocks();
61
+ mockInsertDialog.mockClear();
62
+
63
+ // Clean up any existing dialogs in the DOM
64
+ document.body.innerHTML = '';
65
+
66
+ mockEditor = {
67
+ chain: jest.fn(() => ({
68
+ focus: jest.fn(() => ({
69
+ run: jest.fn(),
70
+ })),
71
+ })),
72
+ };
73
+
74
+ mockUpdateAttributes = jest.fn();
75
+ mockDeleteNode = jest.fn();
76
+ });
77
+
78
+ afterEach(() => {
79
+ // Clean up DOM after each test
80
+ document.body.innerHTML = '';
81
+ });
82
+
83
+ describe('dialog auto-opening behavior - verifies the fix', () => {
84
+ it('should NOT open dialog on mount when audio has existing src', () => {
85
+ const node = {
86
+ attrs: {
87
+ type: 'audio',
88
+ tag: 'audio',
89
+ src: 'https://example.com/audio.mp3',
90
+ width: null,
91
+ height: null,
92
+ },
93
+ };
94
+
95
+ // This should render without opening any dialogs
96
+ const { container } = render(
97
+ <MediaNodeView
98
+ editor={mockEditor}
99
+ node={node}
100
+ updateAttributes={mockUpdateAttributes}
101
+ deleteNode={mockDeleteNode}
102
+ options={{}}
103
+ />
104
+ );
105
+
106
+ // Verify the component rendered successfully
107
+ expect(container.querySelector('audio')).toBeTruthy();
108
+ // Verify mockInsertDialog was not called (dialog didn't open)
109
+ expect(mockInsertDialog).not.toHaveBeenCalled();
110
+ // Verify deleteNode was not called (media was not removed)
111
+ expect(mockDeleteNode).not.toHaveBeenCalled();
112
+ });
113
+
114
+ it('should NOT open dialog on mount when video has existing src', () => {
115
+ const node = {
116
+ attrs: {
117
+ type: 'video',
118
+ tag: 'iframe',
119
+ src: 'https://www.youtube.com/embed/dQw4w9WgXcQ',
120
+ width: '640',
121
+ height: '480',
122
+ },
123
+ };
124
+
125
+ // This should render without opening any dialogs
126
+ const { container } = render(
127
+ <MediaNodeView
128
+ editor={mockEditor}
129
+ node={node}
130
+ updateAttributes={mockUpdateAttributes}
131
+ deleteNode={mockDeleteNode}
132
+ options={{}}
133
+ />
134
+ );
135
+
136
+ // Verify the component rendered successfully
137
+ expect(container.querySelector('iframe')).toBeTruthy();
138
+ // Verify mockInsertDialog was not called (dialog didn't open)
139
+ expect(mockInsertDialog).not.toHaveBeenCalled();
140
+ // Verify deleteNode was not called (media was not removed)
141
+ expect(mockDeleteNode).not.toHaveBeenCalled();
142
+ });
143
+
144
+ it('should render existing media with empty string src without opening dialog', () => {
145
+ const node = {
146
+ attrs: {
147
+ type: 'audio',
148
+ tag: 'audio',
149
+ src: '', // Empty string (falsy but not null)
150
+ width: null,
151
+ height: null,
152
+ },
153
+ };
154
+
155
+ const { container } = render(
156
+ <MediaNodeView
157
+ editor={mockEditor}
158
+ node={node}
159
+ updateAttributes={mockUpdateAttributes}
160
+ deleteNode={mockDeleteNode}
161
+ options={{}}
162
+ />
163
+ );
164
+
165
+ // Empty string is falsy, so dialog WOULD open with current implementation
166
+ // This documents current behavior
167
+ expect(container).toBeTruthy();
168
+ });
169
+ });
170
+
171
+ describe('toolbar interaction', () => {
172
+ it('should render edit and remove buttons', () => {
173
+ const node = {
174
+ attrs: {
175
+ type: 'audio',
176
+ tag: 'audio',
177
+ src: 'https://example.com/audio.mp3',
178
+ },
179
+ };
180
+
181
+ const { getByTestId } = render(
182
+ <MediaNodeView
183
+ editor={mockEditor}
184
+ node={node}
185
+ updateAttributes={mockUpdateAttributes}
186
+ deleteNode={mockDeleteNode}
187
+ options={{}}
188
+ />
189
+ );
190
+
191
+ expect(getByTestId('edit-button')).toBeTruthy();
192
+ expect(getByTestId('remove-button')).toBeTruthy();
193
+ });
194
+ });
195
+
196
+ describe('rendering', () => {
197
+ it('should render audio element when tag is audio', () => {
198
+ const node = {
199
+ attrs: {
200
+ type: 'audio',
201
+ tag: 'audio',
202
+ src: 'https://example.com/audio.mp3',
203
+ },
204
+ };
205
+
206
+ const { container } = render(
207
+ <MediaNodeView
208
+ editor={mockEditor}
209
+ node={node}
210
+ updateAttributes={mockUpdateAttributes}
211
+ deleteNode={mockDeleteNode}
212
+ options={{}}
213
+ />
214
+ );
215
+
216
+ const audio = container.querySelector('audio');
217
+ expect(audio).toBeTruthy();
218
+ const source = audio.querySelector('source');
219
+ expect(source.getAttribute('src')).toBe('https://example.com/audio.mp3');
220
+ });
221
+
222
+ it('should render iframe when tag is iframe', () => {
223
+ const node = {
224
+ attrs: {
225
+ type: 'video',
226
+ tag: 'iframe',
227
+ src: 'https://www.youtube.com/embed/dQw4w9WgXcQ',
228
+ width: '640',
229
+ height: '480',
230
+ },
231
+ };
232
+
233
+ const { container } = render(
234
+ <MediaNodeView
235
+ editor={mockEditor}
236
+ node={node}
237
+ updateAttributes={mockUpdateAttributes}
238
+ deleteNode={mockDeleteNode}
239
+ options={{}}
240
+ />
241
+ );
242
+
243
+ const iframe = container.querySelector('iframe');
244
+ expect(iframe).toBeTruthy();
245
+ expect(iframe.getAttribute('src')).toBe('https://www.youtube.com/embed/dQw4w9WgXcQ');
246
+ });
247
+
248
+ it('should render MediaToolbar', () => {
249
+ const node = {
250
+ attrs: {
251
+ type: 'audio',
252
+ tag: 'audio',
253
+ src: 'https://example.com/audio.mp3',
254
+ },
255
+ };
256
+
257
+ const { getByTestId } = render(
258
+ <MediaNodeView
259
+ editor={mockEditor}
260
+ node={node}
261
+ updateAttributes={mockUpdateAttributes}
262
+ deleteNode={mockDeleteNode}
263
+ options={{}}
264
+ />
265
+ );
266
+
267
+ expect(getByTestId('media-toolbar')).toBeTruthy();
268
+ expect(getByTestId('edit-button')).toBeTruthy();
269
+ expect(getByTestId('remove-button')).toBeTruthy();
270
+ });
271
+
272
+ it('should call deleteNode when remove button is clicked', () => {
273
+ const node = {
274
+ attrs: {
275
+ type: 'audio',
276
+ tag: 'audio',
277
+ src: 'https://example.com/audio.mp3',
278
+ },
279
+ };
280
+
281
+ const { getByTestId } = render(
282
+ <MediaNodeView
283
+ editor={mockEditor}
284
+ node={node}
285
+ updateAttributes={mockUpdateAttributes}
286
+ deleteNode={mockDeleteNode}
287
+ options={{}}
288
+ />
289
+ );
290
+
291
+ fireEvent.click(getByTestId('remove-button'));
292
+
293
+ expect(mockDeleteNode).toHaveBeenCalled();
294
+ });
295
+ });
296
+ });
@@ -268,3 +268,4 @@ describe('insertDialog', () => {
268
268
  expect(dialogs).toHaveLength(1);
269
269
  });
270
270
  });
271
+
@@ -1,6 +1,6 @@
1
1
  import React, { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { isEqual } from 'lodash-es';
3
+ import isEqual from 'lodash-es/isEqual';
4
4
  import debug from 'debug';
5
5
  import LinearProgress from '@mui/material/LinearProgress';
6
6
  import { styled } from '@mui/material/styles';
@@ -1,4 +1,5 @@
1
- import { compact, isEmpty } from 'lodash-es';
1
+ import compact from 'lodash-es/compact';
2
+ import isEmpty from 'lodash-es/isEmpty';
2
3
  import debug from 'debug';
3
4
 
4
5
  const log = debug('@pie-lib:editable-html:plugins');
@@ -30,6 +31,8 @@ export const ALL_PLUGINS = [
30
31
 
31
32
  export const PLUGINS_MAP = {
32
33
  'text-align': 'textAlign',
34
+ 'bulleted-list': 'ul_list',
35
+ 'numbered-list': 'ol_list',
33
36
  };
34
37
 
35
38
  export const DEFAULT_PLUGINS = ALL_PLUGINS.filter((plug) => !['responseArea', 'h3', 'blockquote'].includes(plug));
@@ -153,20 +153,23 @@ export default function MediaNodeView({ editor, node, updateAttributes, deleteNo
153
153
  };
154
154
 
155
155
  useEffect(() => {
156
- insertDialog({
157
- ...node.attrs,
158
- options: options,
159
- edit: true,
160
- callback: (val, data) => {
161
- if (val) {
162
- updateAttributes(data);
163
- } else {
164
- deleteNode();
165
- }
166
-
167
- editor.chain().focus().run();
168
- },
169
- });
156
+ // Only open dialog for newly inserted media without a src
157
+ if (!src) {
158
+ insertDialog({
159
+ ...node.attrs,
160
+ options: options,
161
+ edit: true,
162
+ callback: (val, data) => {
163
+ if (val) {
164
+ updateAttributes(data);
165
+ } else {
166
+ deleteNode();
167
+ }
168
+
169
+ editor.chain().focus().run();
170
+ },
171
+ });
172
+ }
170
173
  }, []);
171
174
 
172
175
  return (