@dotcms/react 0.0.1-alpha.40 → 0.0.1-alpha.42
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/index.esm.d.ts +1 -0
- package/index.esm.js +5164 -0
- package/package.json +8 -4
- package/src/lib/components/BlockEditorRenderer/{BlockEditorRenderer.tsx → BlockEditorRenderer.d.ts} +2 -16
- package/src/lib/components/BlockEditorRenderer/blocks/{Code.tsx → Code.d.ts} +2 -14
- package/src/lib/components/BlockEditorRenderer/blocks/{Contentlet.tsx → Contentlet.d.ts} +1 -21
- package/src/lib/components/BlockEditorRenderer/blocks/Image.d.ts +8 -0
- package/src/lib/components/BlockEditorRenderer/blocks/{Lists.tsx → Lists.d.ts} +3 -12
- package/src/lib/components/BlockEditorRenderer/blocks/Table.d.ts +16 -0
- package/src/lib/components/BlockEditorRenderer/blocks/Texts.d.ts +71 -0
- package/src/lib/components/BlockEditorRenderer/blocks/Video.d.ts +8 -0
- package/src/lib/components/BlockEditorRenderer/item/BlockEditorBlock.d.ts +12 -0
- package/src/lib/components/Column/Column.d.ts +19 -0
- package/src/lib/components/Container/Container.d.ts +19 -0
- package/src/lib/components/DotEditableText/DotEditableText.d.ts +31 -0
- package/src/lib/components/DotEditableText/utils.d.ts +36 -0
- package/src/lib/components/DotcmsLayout/{DotcmsLayout.tsx → DotcmsLayout.d.ts} +2 -18
- package/src/lib/components/PageProvider/{PageProvider.tsx → PageProvider.d.ts} +1 -10
- package/src/lib/components/Row/Row.d.ts +26 -0
- package/src/lib/contexts/{PageContext.tsx → PageContext.d.ts} +2 -4
- package/src/lib/hooks/useDotcmsEditor.d.ts +13 -0
- package/src/lib/hooks/{useDotcmsPageContext.tsx → useDotcmsPageContext.d.ts} +1 -7
- package/src/lib/mocks/mockPageContext.d.ts +8 -0
- package/src/lib/models/{blocks.interface.ts → blocks.interface.d.ts} +16 -19
- package/src/lib/models/{content-node.interface.ts → content-node.interface.d.ts} +4 -12
- package/src/lib/models/{index.ts → index.d.ts} +2 -5
- package/src/lib/utils/utils.d.ts +43 -0
- package/.babelrc +0 -12
- package/.eslintrc.json +0 -18
- package/jest.config.ts +0 -11
- package/project.json +0 -56
- package/src/lib/components/BlockEditorRenderer/BlockEditorRenderer.spec.tsx +0 -51
- package/src/lib/components/BlockEditorRenderer/blocks/Image.tsx +0 -18
- package/src/lib/components/BlockEditorRenderer/blocks/Table.tsx +0 -60
- package/src/lib/components/BlockEditorRenderer/blocks/Texts.tsx +0 -126
- package/src/lib/components/BlockEditorRenderer/blocks/Video.tsx +0 -26
- package/src/lib/components/BlockEditorRenderer/item/BlockEditorBlock.spec.tsx +0 -634
- package/src/lib/components/BlockEditorRenderer/item/BlockEditorBlock.tsx +0 -146
- package/src/lib/components/Column/Column.module.css +0 -99
- package/src/lib/components/Column/Column.spec.tsx +0 -78
- package/src/lib/components/Column/Column.tsx +0 -59
- package/src/lib/components/Container/Container.module.css +0 -7
- package/src/lib/components/Container/Container.spec.tsx +0 -155
- package/src/lib/components/Container/Container.tsx +0 -122
- package/src/lib/components/DotEditableText/DotEditableText.spec.tsx +0 -232
- package/src/lib/components/DotEditableText/DotEditableText.tsx +0 -168
- package/src/lib/components/DotEditableText/utils.ts +0 -82
- package/src/lib/components/DotcmsLayout/DotcmsLayout.module.css +0 -7
- package/src/lib/components/DotcmsLayout/DotcmsLayout.spec.tsx +0 -46
- package/src/lib/components/PageProvider/PageProvider.module.css +0 -7
- package/src/lib/components/PageProvider/PageProvider.spec.tsx +0 -59
- package/src/lib/components/Row/Row.module.css +0 -5
- package/src/lib/components/Row/Row.spec.tsx +0 -92
- package/src/lib/components/Row/Row.tsx +0 -52
- package/src/lib/hooks/useDotcmsEditor.spec.ts +0 -176
- package/src/lib/hooks/useDotcmsEditor.ts +0 -94
- package/src/lib/hooks/useDotcmsPageContext.spec.tsx +0 -47
- package/src/lib/mocks/mockPageContext.tsx +0 -113
- package/src/lib/utils/utils.ts +0 -89
- package/tsconfig.json +0 -20
- package/tsconfig.lib.json +0 -23
- package/tsconfig.spec.json +0 -20
- /package/src/{index.ts → index.d.ts} +0 -0
- /package/src/lib/mocks/{index.ts → index.d.ts} +0 -0
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
import { expect } from '@jest/globals';
|
|
2
|
-
import { fireEvent, render, screen } from '@testing-library/react';
|
|
3
|
-
import * as tinymceReact from '@tinymce/tinymce-react';
|
|
4
|
-
|
|
5
|
-
import * as dotcmsClient from '@dotcms/client';
|
|
6
|
-
|
|
7
|
-
import { DotEditableText } from './DotEditableText';
|
|
8
|
-
|
|
9
|
-
import { dotcmsContentletMock } from '../../mocks/mockPageContext';
|
|
10
|
-
|
|
11
|
-
const { CUSTOMER_ACTIONS, postMessageToEditor } = dotcmsClient;
|
|
12
|
-
|
|
13
|
-
// Define mockEditor before using it in jest.mock
|
|
14
|
-
const TINYMCE_EDITOR_MOCK = {
|
|
15
|
-
focus: () => {
|
|
16
|
-
/* */
|
|
17
|
-
},
|
|
18
|
-
getContent: (_data: string) => '',
|
|
19
|
-
isDirty: () => false,
|
|
20
|
-
hasFocus: () => false,
|
|
21
|
-
setContent: () => {
|
|
22
|
-
/* */
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
jest.mock('@tinymce/tinymce-react', () => ({
|
|
27
|
-
Editor: jest.fn(({ onInit, onMouseDown, onFocusOut }) => {
|
|
28
|
-
onInit({}, TINYMCE_EDITOR_MOCK);
|
|
29
|
-
|
|
30
|
-
return <div data-testid="tinymce-editor" onMouseDown={onMouseDown} onBlur={onFocusOut} />;
|
|
31
|
-
})
|
|
32
|
-
}));
|
|
33
|
-
|
|
34
|
-
// Mock @dotcms/client module
|
|
35
|
-
jest.mock('@dotcms/client', () => ({
|
|
36
|
-
...jest.requireActual('@dotcms/client'),
|
|
37
|
-
isInsideEditor: jest.fn().mockImplementation(() => true),
|
|
38
|
-
postMessageToEditor: jest.fn(),
|
|
39
|
-
DotCmsClient: {
|
|
40
|
-
dotcmsUrl: 'http://localhost:8080'
|
|
41
|
-
}
|
|
42
|
-
}));
|
|
43
|
-
|
|
44
|
-
const mockedDotcmsClient = dotcmsClient as jest.Mocked<typeof dotcmsClient>;
|
|
45
|
-
const { Editor } = tinymceReact as jest.Mocked<typeof tinymceReact>;
|
|
46
|
-
|
|
47
|
-
describe('DotEditableText', () => {
|
|
48
|
-
describe('Outside editor', () => {
|
|
49
|
-
beforeEach(() => {
|
|
50
|
-
mockedDotcmsClient.isInsideEditor.mockReturnValue(false);
|
|
51
|
-
render(<DotEditableText contentlet={dotcmsContentletMock} fieldName="title" />);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('should render the content', () => {
|
|
55
|
-
const editor = screen.queryByTestId('tinymce-editor');
|
|
56
|
-
expect(editor).toBeNull();
|
|
57
|
-
expect(screen.getByText(dotcmsContentletMock['title'])).not.toBeNull();
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe('Inside editor', () => {
|
|
62
|
-
let rerenderFn: (ui: React.ReactNode) => void;
|
|
63
|
-
|
|
64
|
-
beforeEach(() => {
|
|
65
|
-
mockedDotcmsClient.isInsideEditor.mockReturnValue(true);
|
|
66
|
-
const { rerender } = render(
|
|
67
|
-
<DotEditableText contentlet={dotcmsContentletMock} fieldName="title" />
|
|
68
|
-
);
|
|
69
|
-
rerenderFn = rerender;
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('should pass the correct props to the Editor component', () => {
|
|
73
|
-
const editor = screen.getByTestId('tinymce-editor');
|
|
74
|
-
expect(editor).not.toBeNull();
|
|
75
|
-
|
|
76
|
-
expect(Editor).toHaveBeenCalledWith(
|
|
77
|
-
{
|
|
78
|
-
tinymceScriptSrc: 'http://localhost:8080/ext/tinymcev7/tinymce.min.js',
|
|
79
|
-
inline: true,
|
|
80
|
-
init: {
|
|
81
|
-
inline: true,
|
|
82
|
-
menubar: false,
|
|
83
|
-
plugins: '',
|
|
84
|
-
powerpaste_html_import: 'clean',
|
|
85
|
-
powerpaste_word_import: 'clean',
|
|
86
|
-
suffix: '.min',
|
|
87
|
-
toolbar: '',
|
|
88
|
-
valid_styles: {
|
|
89
|
-
'*': 'font-size,font-family,color,text-decoration,text-align'
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
initialValue: dotcmsContentletMock.title,
|
|
93
|
-
onMouseDown: expect.any(Function),
|
|
94
|
-
onFocusOut: expect.any(Function),
|
|
95
|
-
onInit: expect.any(Function)
|
|
96
|
-
},
|
|
97
|
-
{}
|
|
98
|
-
);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
describe('DotEditableText events', () => {
|
|
102
|
-
let focusSpy: jest.SpyInstance;
|
|
103
|
-
|
|
104
|
-
describe('Window Message', () => {
|
|
105
|
-
beforeEach(() => {
|
|
106
|
-
focusSpy = jest.spyOn(TINYMCE_EDITOR_MOCK, 'focus');
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it("should focus on the editor when the message is 'COPY_CONTENTLET_INLINE_EDITING_SUCCESS'", () => {
|
|
110
|
-
window.dispatchEvent(
|
|
111
|
-
new MessageEvent('message', {
|
|
112
|
-
data: {
|
|
113
|
-
name: 'COPY_CONTENTLET_INLINE_EDITING_SUCCESS',
|
|
114
|
-
payload: {
|
|
115
|
-
oldInode: dotcmsContentletMock['inode'],
|
|
116
|
-
inode: '456'
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
})
|
|
120
|
-
);
|
|
121
|
-
expect(focusSpy).toHaveBeenCalled();
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it("should not focus on the editor when the message is not 'COPY_CONTENTLET_INLINE_EDITING_SUCCESS'", () => {
|
|
125
|
-
window.dispatchEvent(
|
|
126
|
-
new MessageEvent('message', {
|
|
127
|
-
data: { name: 'ANOTHER_EVENT' }
|
|
128
|
-
})
|
|
129
|
-
);
|
|
130
|
-
expect(focusSpy).not.toHaveBeenCalled();
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
describe('mousedown', () => {
|
|
135
|
-
const event = new MouseEvent('mousedown', {
|
|
136
|
-
bubbles: true
|
|
137
|
-
});
|
|
138
|
-
const mutiplePagesContentlet = {
|
|
139
|
-
...dotcmsContentletMock,
|
|
140
|
-
onNumberOfPages: 2
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
it('should postMessage the UVE if the content is in multiple pages', () => {
|
|
144
|
-
rerenderFn(
|
|
145
|
-
<DotEditableText contentlet={mutiplePagesContentlet} fieldName="title" />
|
|
146
|
-
);
|
|
147
|
-
const editorElem = screen.getByTestId('tinymce-editor');
|
|
148
|
-
fireEvent(editorElem, event);
|
|
149
|
-
|
|
150
|
-
const payload = {
|
|
151
|
-
dataset: {
|
|
152
|
-
fieldName: 'title',
|
|
153
|
-
inode: mutiplePagesContentlet.inode,
|
|
154
|
-
language: mutiplePagesContentlet.languageId
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
expect(postMessageToEditor).toHaveBeenCalledWith({
|
|
158
|
-
action: CUSTOMER_ACTIONS.COPY_CONTENTLET_INLINE_EDITING,
|
|
159
|
-
payload
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it('should not postMessage the UVE if the content is in a single page', () => {
|
|
164
|
-
const editorElem = screen.getByTestId('tinymce-editor');
|
|
165
|
-
fireEvent(editorElem, event);
|
|
166
|
-
expect(postMessageToEditor).not.toHaveBeenCalled();
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
describe('onFocusOut', () => {
|
|
171
|
-
let isDirtySpy: jest.SpyInstance;
|
|
172
|
-
let getContentSpy: jest.SpyInstance;
|
|
173
|
-
|
|
174
|
-
const event = new FocusEvent('focusout', {
|
|
175
|
-
bubbles: true
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
beforeEach(() => {
|
|
179
|
-
isDirtySpy = jest.spyOn(TINYMCE_EDITOR_MOCK, 'isDirty');
|
|
180
|
-
getContentSpy = jest.spyOn(TINYMCE_EDITOR_MOCK, 'getContent');
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('should not postMessage the UVE if the editor is not dirty', () => {
|
|
184
|
-
mockedDotcmsClient.isInsideEditor.mockReturnValue(false);
|
|
185
|
-
const editorElem = screen.getByTestId('tinymce-editor');
|
|
186
|
-
fireEvent(editorElem, event);
|
|
187
|
-
expect(isDirtySpy).toHaveBeenCalled();
|
|
188
|
-
expect(getContentSpy).toHaveBeenCalledWith({ format: 'text' });
|
|
189
|
-
expect(postMessageToEditor).not.toHaveBeenCalled();
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it('should not postMessage the UVE if the content did not change', () => {
|
|
193
|
-
isDirtySpy.mockReturnValue(true);
|
|
194
|
-
getContentSpy.mockReturnValue(dotcmsContentletMock.title);
|
|
195
|
-
|
|
196
|
-
const editorElem = screen.getByTestId('tinymce-editor');
|
|
197
|
-
fireEvent(editorElem, event);
|
|
198
|
-
|
|
199
|
-
expect(isDirtySpy).toHaveBeenCalled();
|
|
200
|
-
expect(getContentSpy).toHaveBeenCalledWith({ format: 'text' });
|
|
201
|
-
expect(postMessageToEditor).not.toHaveBeenCalled();
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it('should postMessage the UVE if the content changed', () => {
|
|
205
|
-
isDirtySpy.mockReturnValue(true);
|
|
206
|
-
getContentSpy.mockReturnValue('New content');
|
|
207
|
-
|
|
208
|
-
const editorElem = screen.getByTestId('tinymce-editor');
|
|
209
|
-
fireEvent(editorElem, event);
|
|
210
|
-
|
|
211
|
-
const postMessageData = {
|
|
212
|
-
action: CUSTOMER_ACTIONS.UPDATE_CONTENTLET_INLINE_EDITING,
|
|
213
|
-
payload: {
|
|
214
|
-
content: 'New content',
|
|
215
|
-
dataset: {
|
|
216
|
-
inode: dotcmsContentletMock.inode,
|
|
217
|
-
langId: dotcmsContentletMock.languageId,
|
|
218
|
-
fieldName: 'title'
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
expect(isDirtySpy).toHaveBeenCalled();
|
|
224
|
-
expect(getContentSpy).toHaveBeenCalledWith({ format: 'text' });
|
|
225
|
-
expect(postMessageToEditor).toHaveBeenCalledWith(postMessageData);
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
afterEach(() => jest.clearAllMocks()); // Clear all mocks to avoid side effects from other tests
|
|
232
|
-
});
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
import { Editor } from '@tinymce/tinymce-react';
|
|
2
|
-
import { useEffect, useRef, useState } from 'react';
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
isInsideEditor as isInsideEditorFn,
|
|
6
|
-
postMessageToEditor,
|
|
7
|
-
CUSTOMER_ACTIONS,
|
|
8
|
-
DotCmsClient
|
|
9
|
-
} from '@dotcms/client';
|
|
10
|
-
|
|
11
|
-
import { DotEditableTextProps, TINYMCE_CONFIG } from './utils';
|
|
12
|
-
|
|
13
|
-
const MCE_URL = '/ext/tinymcev7/tinymce.min.js';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Allows inline edit content pulled from dotCMS API using TinyMCE editor
|
|
17
|
-
*
|
|
18
|
-
* @export
|
|
19
|
-
* @component
|
|
20
|
-
* @param {Readonly<DotEditableTextProps>} props {
|
|
21
|
-
* mode = 'plain',
|
|
22
|
-
* format = 'text',
|
|
23
|
-
* contentlet,
|
|
24
|
-
* fieldName = ''
|
|
25
|
-
* }
|
|
26
|
-
* @example
|
|
27
|
-
* ```javascript
|
|
28
|
-
* import { DotEditableText } from '@dotcms/react';
|
|
29
|
-
*
|
|
30
|
-
* const MyContentletWithTitle = ({ contentlet }) => (
|
|
31
|
-
* <h2>
|
|
32
|
-
* <DotEditableText
|
|
33
|
-
* contentlet={contentlet}
|
|
34
|
-
* fieldName="title"
|
|
35
|
-
* mode='full'
|
|
36
|
-
* format='text'/>
|
|
37
|
-
* </h2>
|
|
38
|
-
* );
|
|
39
|
-
* ```
|
|
40
|
-
* @returns {JSX.Element} A component to edit content inline
|
|
41
|
-
*/
|
|
42
|
-
export function DotEditableText({
|
|
43
|
-
mode = 'plain',
|
|
44
|
-
format = 'text',
|
|
45
|
-
contentlet,
|
|
46
|
-
fieldName = ''
|
|
47
|
-
}: Readonly<DotEditableTextProps>): JSX.Element {
|
|
48
|
-
const editorRef = useRef<Editor['editor'] | null>(null);
|
|
49
|
-
const [scriptSrc, setScriptSrc] = useState('');
|
|
50
|
-
const [isInsideEditor, setIsInsideEditor] = useState(false);
|
|
51
|
-
const [content, setContent] = useState(contentlet?.[fieldName] || '');
|
|
52
|
-
|
|
53
|
-
useEffect(() => {
|
|
54
|
-
setIsInsideEditor(isInsideEditorFn());
|
|
55
|
-
|
|
56
|
-
if (!contentlet || !fieldName) {
|
|
57
|
-
console.error('DotEditableText: contentlet or fieldName is missing');
|
|
58
|
-
console.error('Ensure that all needed props are passed to view and edit the content');
|
|
59
|
-
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (!isInsideEditorFn()) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const createURL = new URL(MCE_URL, DotCmsClient.dotcmsUrl);
|
|
68
|
-
setScriptSrc(createURL.toString());
|
|
69
|
-
|
|
70
|
-
const content = contentlet?.[fieldName] || '';
|
|
71
|
-
editorRef.current?.setContent(content, { format });
|
|
72
|
-
setContent(content);
|
|
73
|
-
}, [format, fieldName, contentlet]);
|
|
74
|
-
|
|
75
|
-
useEffect(() => {
|
|
76
|
-
if (!isInsideEditorFn()) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const onMessage = ({ data }: MessageEvent) => {
|
|
81
|
-
const { name, payload } = data;
|
|
82
|
-
if (name !== 'COPY_CONTENTLET_INLINE_EDITING_SUCCESS') {
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const { oldInode, inode } = payload;
|
|
87
|
-
const currentInode = contentlet.inode;
|
|
88
|
-
const shouldFocus = currentInode === oldInode || currentInode === inode;
|
|
89
|
-
|
|
90
|
-
if (shouldFocus) {
|
|
91
|
-
editorRef.current?.focus();
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
window.addEventListener('message', onMessage);
|
|
96
|
-
|
|
97
|
-
return () => {
|
|
98
|
-
window.removeEventListener('message', onMessage);
|
|
99
|
-
};
|
|
100
|
-
}, [contentlet.inode]);
|
|
101
|
-
|
|
102
|
-
const onMouseDown = (event: MouseEvent) => {
|
|
103
|
-
const { onNumberOfPages = 1 } = contentlet;
|
|
104
|
-
const { inode, languageId: language } = contentlet;
|
|
105
|
-
|
|
106
|
-
if (onNumberOfPages <= 1 || editorRef.current?.hasFocus()) {
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
event.stopPropagation();
|
|
111
|
-
event.preventDefault();
|
|
112
|
-
|
|
113
|
-
postMessageToEditor({
|
|
114
|
-
action: CUSTOMER_ACTIONS.COPY_CONTENTLET_INLINE_EDITING,
|
|
115
|
-
payload: {
|
|
116
|
-
dataset: {
|
|
117
|
-
inode,
|
|
118
|
-
language,
|
|
119
|
-
fieldName
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
const onFocusOut = () => {
|
|
126
|
-
const editedContent = editorRef.current?.getContent({ format: format }) || '';
|
|
127
|
-
const { inode, languageId: langId } = contentlet;
|
|
128
|
-
|
|
129
|
-
if (!editorRef.current?.isDirty() || !didContentChange(editedContent)) {
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
postMessageToEditor({
|
|
134
|
-
action: CUSTOMER_ACTIONS.UPDATE_CONTENTLET_INLINE_EDITING,
|
|
135
|
-
payload: {
|
|
136
|
-
content: editedContent,
|
|
137
|
-
dataset: {
|
|
138
|
-
inode,
|
|
139
|
-
langId,
|
|
140
|
-
fieldName
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
const didContentChange = (editedContent: string) => {
|
|
147
|
-
return content !== editedContent;
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
if (!isInsideEditor) {
|
|
151
|
-
// We can let the user pass the Child Component and create a root to get the HTML for the editor
|
|
152
|
-
return <span dangerouslySetInnerHTML={{ __html: content }} />;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return (
|
|
156
|
-
<Editor
|
|
157
|
-
tinymceScriptSrc={scriptSrc}
|
|
158
|
-
inline={true}
|
|
159
|
-
onInit={(_, editor) => (editorRef.current = editor)}
|
|
160
|
-
init={TINYMCE_CONFIG[mode]}
|
|
161
|
-
initialValue={content}
|
|
162
|
-
onMouseDown={onMouseDown}
|
|
163
|
-
onFocusOut={onFocusOut}
|
|
164
|
-
/>
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export default DotEditableText;
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { IAllProps } from '@tinymce/tinymce-react';
|
|
2
|
-
|
|
3
|
-
import { DotCMSContentlet } from '../../models';
|
|
4
|
-
|
|
5
|
-
export type DOT_EDITABLE_TEXT_FORMAT = 'html' | 'text';
|
|
6
|
-
|
|
7
|
-
export type DOT_EDITABLE_TEXT_MODE = 'minimal' | 'full' | 'plain';
|
|
8
|
-
|
|
9
|
-
export interface DotEditableTextProps {
|
|
10
|
-
/**
|
|
11
|
-
* Represents the field name of the `contentlet` that can be edited
|
|
12
|
-
*
|
|
13
|
-
* @memberof DotEditableTextProps
|
|
14
|
-
*/
|
|
15
|
-
fieldName: string;
|
|
16
|
-
/**
|
|
17
|
-
* Represents the format of the editor which can be `text` or `html`
|
|
18
|
-
*
|
|
19
|
-
* @type {DOT_EDITABLE_TEXT_FORMAT}
|
|
20
|
-
* @memberof DotEditableTextProps
|
|
21
|
-
*/
|
|
22
|
-
format?: DOT_EDITABLE_TEXT_FORMAT;
|
|
23
|
-
/**
|
|
24
|
-
* Represents the mode of the editor which can be `plain`, `minimal`, or `full`
|
|
25
|
-
*
|
|
26
|
-
* @type {DOT_EDITABLE_TEXT_MODE}
|
|
27
|
-
* @memberof DotEditableTextProps
|
|
28
|
-
*/
|
|
29
|
-
mode?: DOT_EDITABLE_TEXT_MODE;
|
|
30
|
-
/**
|
|
31
|
-
* Represents the `contentlet` that can be inline edited
|
|
32
|
-
*
|
|
33
|
-
* @type {DotCMSContentlet}
|
|
34
|
-
* @memberof DotEditableTextProps
|
|
35
|
-
*/
|
|
36
|
-
contentlet: DotCMSContentlet;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const DEFAULT_TINYMCE_CONFIG: IAllProps['init'] = {
|
|
40
|
-
inline: true,
|
|
41
|
-
menubar: false,
|
|
42
|
-
powerpaste_html_import: 'clean',
|
|
43
|
-
powerpaste_word_import: 'clean',
|
|
44
|
-
suffix: '.min',
|
|
45
|
-
valid_styles: {
|
|
46
|
-
'*': 'font-size,font-family,color,text-decoration,text-align'
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
export const TINYMCE_CONFIG: {
|
|
51
|
-
[key in DOT_EDITABLE_TEXT_MODE]: IAllProps['init'];
|
|
52
|
-
} = {
|
|
53
|
-
full: {
|
|
54
|
-
...DEFAULT_TINYMCE_CONFIG,
|
|
55
|
-
plugins: 'link lists autolink charmap',
|
|
56
|
-
toolbar: [
|
|
57
|
-
'styleselect undo redo | bold italic underline | forecolor backcolor | alignleft aligncenter alignright alignfull | numlist bullist outdent indent | hr charmap removeformat | link'
|
|
58
|
-
],
|
|
59
|
-
style_formats: [
|
|
60
|
-
{ title: 'Paragraph', format: 'p' },
|
|
61
|
-
{ title: 'Header 1', format: 'h1' },
|
|
62
|
-
{ title: 'Header 2', format: 'h2' },
|
|
63
|
-
{ title: 'Header 3', format: 'h3' },
|
|
64
|
-
{ title: 'Header 4', format: 'h4' },
|
|
65
|
-
{ title: 'Header 5', format: 'h5' },
|
|
66
|
-
{ title: 'Header 6', format: 'h6' },
|
|
67
|
-
{ title: 'Pre', format: 'pre' },
|
|
68
|
-
{ title: 'Code', format: 'code' }
|
|
69
|
-
]
|
|
70
|
-
},
|
|
71
|
-
plain: {
|
|
72
|
-
...DEFAULT_TINYMCE_CONFIG,
|
|
73
|
-
plugins: '',
|
|
74
|
-
toolbar: ''
|
|
75
|
-
},
|
|
76
|
-
minimal: {
|
|
77
|
-
...DEFAULT_TINYMCE_CONFIG,
|
|
78
|
-
plugins: 'link autolink',
|
|
79
|
-
toolbar: 'bold italic underline | link',
|
|
80
|
-
valid_elements: 'strong,em,span[style],a[href]'
|
|
81
|
-
}
|
|
82
|
-
};
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import '@testing-library/jest-dom';
|
|
2
|
-
import { render, screen } from '@testing-library/react';
|
|
3
|
-
import { ElementRef, ForwardRefExoticComponent } from 'react';
|
|
4
|
-
|
|
5
|
-
import { DotcmsLayout } from './DotcmsLayout';
|
|
6
|
-
|
|
7
|
-
import { mockPageContext } from '../../mocks/mockPageContext';
|
|
8
|
-
|
|
9
|
-
jest.mock('../Row/Row', () => {
|
|
10
|
-
const { forwardRef } = jest.requireActual('react');
|
|
11
|
-
|
|
12
|
-
return {
|
|
13
|
-
Row: forwardRef(
|
|
14
|
-
(
|
|
15
|
-
{ children }: { children: JSX.Element },
|
|
16
|
-
ref: ElementRef<ForwardRefExoticComponent<unknown>>
|
|
17
|
-
) => (
|
|
18
|
-
<div data-testid="mockRow" ref={ref}>
|
|
19
|
-
{children}
|
|
20
|
-
</div>
|
|
21
|
-
)
|
|
22
|
-
)
|
|
23
|
-
};
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
jest.mock('../PageProvider/PageProvider', () => {
|
|
27
|
-
return {
|
|
28
|
-
PageProvider: ({ children }: { children: JSX.Element }) => (
|
|
29
|
-
<div data-testid="mockPageProvider">{children}</div>
|
|
30
|
-
)
|
|
31
|
-
};
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
describe('DotcmsLayout', () => {
|
|
35
|
-
it('renders correctly with PageProvider and rows', () => {
|
|
36
|
-
render(
|
|
37
|
-
<DotcmsLayout
|
|
38
|
-
pageContext={{ ...mockPageContext, isInsideEditor: true }}
|
|
39
|
-
config={{ pathname: 'some-url' }}
|
|
40
|
-
/>
|
|
41
|
-
);
|
|
42
|
-
expect(screen.getAllByTestId('mockRow').length).toBe(
|
|
43
|
-
mockPageContext.pageAsset.layout.body.rows.length
|
|
44
|
-
);
|
|
45
|
-
});
|
|
46
|
-
});
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { render, screen } from '@testing-library/react';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
|
|
4
|
-
import '@testing-library/jest-dom';
|
|
5
|
-
import { PageProvider } from './PageProvider';
|
|
6
|
-
|
|
7
|
-
import { PageContext } from '../../contexts/PageContext';
|
|
8
|
-
|
|
9
|
-
const MockChildComponent = () => {
|
|
10
|
-
const context = React.useContext(PageContext);
|
|
11
|
-
|
|
12
|
-
return <div data-testid="mockChild">{JSON.stringify(context?.pageAsset.page.title)}</div>;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
describe('PageProvider', () => {
|
|
16
|
-
const mockEntity = {
|
|
17
|
-
pageAsset: {
|
|
18
|
-
page: {
|
|
19
|
-
title: 'Test Page',
|
|
20
|
-
identifier: 'test-page-1'
|
|
21
|
-
}
|
|
22
|
-
// ... add other context properties as needed
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
it('provides the context to its children', () => {
|
|
27
|
-
render(
|
|
28
|
-
<PageProvider pageContext={mockEntity}>
|
|
29
|
-
<MockChildComponent />
|
|
30
|
-
</PageProvider>
|
|
31
|
-
);
|
|
32
|
-
expect(screen.getByTestId('mockChild')).toHaveTextContent(mockEntity.pageAsset.page.title);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('updates context when entity changes', () => {
|
|
36
|
-
const { rerender } = render(
|
|
37
|
-
<PageProvider pageContext={mockEntity}>
|
|
38
|
-
<MockChildComponent />
|
|
39
|
-
</PageProvider>
|
|
40
|
-
);
|
|
41
|
-
// Change the context
|
|
42
|
-
const newEntity = {
|
|
43
|
-
...mockEntity,
|
|
44
|
-
pageAsset: {
|
|
45
|
-
...mockEntity.pageAsset,
|
|
46
|
-
page: {
|
|
47
|
-
...mockEntity.pageAsset.page,
|
|
48
|
-
title: 'Updated Test Page'
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
rerender(
|
|
53
|
-
<PageProvider pageContext={newEntity}>
|
|
54
|
-
<MockChildComponent />
|
|
55
|
-
</PageProvider>
|
|
56
|
-
);
|
|
57
|
-
expect(screen.getByTestId('mockChild')).toHaveTextContent(newEntity.pageAsset.page.title);
|
|
58
|
-
});
|
|
59
|
-
});
|