@dotcms/react 0.0.1-alpha.37 → 0.0.1-alpha.39
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/.babelrc +12 -0
- package/.eslintrc.json +18 -0
- package/jest.config.ts +11 -0
- package/package.json +4 -8
- package/project.json +56 -0
- package/src/lib/components/BlockEditorRenderer/BlockEditorRenderer.spec.tsx +51 -0
- package/src/lib/components/BlockEditorRenderer/{BlockEditorRenderer.d.ts → BlockEditorRenderer.tsx} +16 -2
- package/src/lib/components/BlockEditorRenderer/blocks/{Code.d.ts → Code.tsx} +14 -2
- package/src/lib/components/BlockEditorRenderer/blocks/{Contentlet.d.ts → Contentlet.tsx} +21 -1
- package/src/lib/components/BlockEditorRenderer/blocks/Image.tsx +18 -0
- package/src/lib/components/BlockEditorRenderer/blocks/{Lists.d.ts → Lists.tsx} +12 -3
- package/src/lib/components/BlockEditorRenderer/blocks/Table.tsx +60 -0
- package/src/lib/components/BlockEditorRenderer/blocks/Texts.tsx +126 -0
- package/src/lib/components/BlockEditorRenderer/blocks/Video.tsx +26 -0
- package/src/lib/components/BlockEditorRenderer/item/BlockEditorBlock.spec.tsx +634 -0
- package/src/lib/components/BlockEditorRenderer/item/BlockEditorBlock.tsx +146 -0
- package/src/lib/components/Column/Column.module.css +99 -0
- package/src/lib/components/Column/Column.spec.tsx +78 -0
- package/src/lib/components/Column/Column.tsx +59 -0
- package/src/lib/components/Container/Container.module.css +7 -0
- package/src/lib/components/Container/Container.spec.tsx +155 -0
- package/src/lib/components/Container/Container.tsx +122 -0
- package/src/lib/components/DotEditableText/DotEditableText.spec.tsx +232 -0
- package/src/lib/components/DotEditableText/DotEditableText.tsx +168 -0
- package/src/lib/components/DotEditableText/utils.ts +82 -0
- package/src/lib/components/DotcmsLayout/DotcmsLayout.module.css +7 -0
- package/src/lib/components/DotcmsLayout/DotcmsLayout.spec.tsx +46 -0
- package/src/lib/components/DotcmsLayout/{DotcmsLayout.d.ts → DotcmsLayout.tsx} +18 -2
- package/src/lib/components/PageProvider/PageProvider.module.css +7 -0
- package/src/lib/components/PageProvider/PageProvider.spec.tsx +59 -0
- package/src/lib/components/PageProvider/{PageProvider.d.ts → PageProvider.tsx} +10 -1
- package/src/lib/components/Row/Row.module.css +5 -0
- package/src/lib/components/Row/Row.spec.tsx +92 -0
- package/src/lib/components/Row/Row.tsx +52 -0
- package/src/lib/contexts/PageContext.tsx +10 -0
- package/src/lib/hooks/useDotcmsEditor.spec.ts +176 -0
- package/src/lib/hooks/useDotcmsEditor.ts +94 -0
- package/src/lib/hooks/useDotcmsPageContext.spec.tsx +47 -0
- package/src/lib/hooks/{useDotcmsPageContext.d.ts → useDotcmsPageContext.tsx} +7 -1
- package/src/lib/mocks/mockPageContext.tsx +113 -0
- package/src/lib/models/{blocks.interface.d.ts → blocks.interface.ts} +43 -16
- package/src/lib/models/content-node.interface.ts +90 -0
- package/src/lib/models/{index.d.ts → index.ts} +5 -2
- package/src/lib/utils/utils.ts +89 -0
- package/tsconfig.json +20 -0
- package/tsconfig.lib.json +23 -0
- package/tsconfig.spec.json +20 -0
- package/index.esm.d.ts +0 -1
- package/index.esm.js +0 -5070
- package/src/lib/components/BlockEditorRenderer/blocks/Image.d.ts +0 -8
- package/src/lib/components/BlockEditorRenderer/blocks/Table.d.ts +0 -16
- package/src/lib/components/BlockEditorRenderer/blocks/Texts.d.ts +0 -71
- package/src/lib/components/BlockEditorRenderer/blocks/Video.d.ts +0 -8
- package/src/lib/components/BlockEditorRenderer/item/BlockEditorBlock.d.ts +0 -12
- package/src/lib/components/Column/Column.d.ts +0 -5
- package/src/lib/components/Container/Container.d.ts +0 -5
- package/src/lib/components/DotEditableText/DotEditableText.d.ts +0 -3
- package/src/lib/components/DotEditableText/utils.d.ts +0 -36
- package/src/lib/components/Row/Row.d.ts +0 -25
- package/src/lib/contexts/PageContext.d.ts +0 -3
- package/src/lib/hooks/useDotcmsEditor.d.ts +0 -3
- package/src/lib/mocks/mockPageContext.d.ts +0 -8
- package/src/lib/models/content-node.interface.d.ts +0 -29
- package/src/lib/utils/utils.d.ts +0 -23
- /package/src/{index.d.ts → index.ts} +0 -0
- /package/src/lib/mocks/{index.d.ts → index.ts} +0 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
|
|
3
|
+
import { Row } from './Row';
|
|
4
|
+
|
|
5
|
+
import { MockContextRender } from '../../mocks/mockPageContext';
|
|
6
|
+
import { ColumnProps } from '../Column/Column';
|
|
7
|
+
import { DotCMSPageContext } from '../PageProvider/PageProvider';
|
|
8
|
+
|
|
9
|
+
import '@testing-library/jest-dom';
|
|
10
|
+
|
|
11
|
+
jest.mock('../Column/Column', () => {
|
|
12
|
+
return {
|
|
13
|
+
Column: ({ column }: ColumnProps) => (
|
|
14
|
+
<div data-testid="mockColumn">{JSON.stringify(column)}</div>
|
|
15
|
+
)
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('Row', () => {
|
|
20
|
+
const mockRowData: DotCMSPageContext['layout']['body']['rows'][0] = {
|
|
21
|
+
columns: [
|
|
22
|
+
{
|
|
23
|
+
width: 60,
|
|
24
|
+
leftOffset: 2,
|
|
25
|
+
containers: [
|
|
26
|
+
{
|
|
27
|
+
identifier: '123',
|
|
28
|
+
uuid: '1'
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
styleClass: ''
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
width: 20,
|
|
35
|
+
leftOffset: 0,
|
|
36
|
+
containers: [
|
|
37
|
+
{
|
|
38
|
+
identifier: '456',
|
|
39
|
+
uuid: '2'
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
styleClass: ''
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
styleClass: ''
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
describe('row is inside editor', () => {
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
render(
|
|
51
|
+
<MockContextRender mockContext={{ isInsideEditor: true }}>
|
|
52
|
+
<Row row={mockRowData} />
|
|
53
|
+
</MockContextRender>
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should set the data-dot attribute', () => {
|
|
58
|
+
expect(screen.getByTestId('row')).toHaveAttribute('data-dot', 'row');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('renders the correct number of mock columns', () => {
|
|
62
|
+
const mockColumns = screen.getAllByTestId('mockColumn');
|
|
63
|
+
expect(mockColumns.length).toBe(mockRowData.columns.length);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('passes the correct props to each mock Column', () => {
|
|
67
|
+
mockRowData.columns.forEach((column, index) => {
|
|
68
|
+
expect(screen.getAllByTestId('mockColumn')[index].innerHTML).toBe(
|
|
69
|
+
JSON.stringify(column)
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('renders the correct number of columns', () => {
|
|
75
|
+
const columns = screen.getAllByTestId('mockColumn');
|
|
76
|
+
expect(columns.length).toBe(mockRowData.columns.length);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe('row is not inside editor', () => {
|
|
80
|
+
beforeEach(() => {
|
|
81
|
+
render(
|
|
82
|
+
<MockContextRender mockContext={{ isInsideEditor: false }}>
|
|
83
|
+
<Row row={mockRowData} />
|
|
84
|
+
</MockContextRender>
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should not have dot attr', () => {
|
|
89
|
+
expect(screen.queryByTestId('row')).toBeNull();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { forwardRef, useContext } from 'react';
|
|
2
|
+
|
|
3
|
+
import styles from './row.module.css';
|
|
4
|
+
|
|
5
|
+
import { PageContext } from '../../contexts/PageContext';
|
|
6
|
+
import { DotCMSPageContext } from '../../models';
|
|
7
|
+
import { combineClasses } from '../../utils/utils';
|
|
8
|
+
import { Column } from '../Column/Column';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Props for the row component
|
|
12
|
+
*
|
|
13
|
+
* @interface RowProps
|
|
14
|
+
*
|
|
15
|
+
*/
|
|
16
|
+
export interface RowProps {
|
|
17
|
+
/**
|
|
18
|
+
* Row data
|
|
19
|
+
*
|
|
20
|
+
* @type {DotCMSPageContext['layout']['body']['rows'][0]}
|
|
21
|
+
* @memberof RowProps
|
|
22
|
+
*/
|
|
23
|
+
row: DotCMSPageContext['pageAsset']['layout']['body']['rows'][0];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* This component renders a row with all it's content using the layout provided by dotCMS Page API.
|
|
28
|
+
*
|
|
29
|
+
* @see {@link https://www.dotcms.com/docs/latest/page-rest-api-layout-as-a-service-laas}
|
|
30
|
+
* @category Components
|
|
31
|
+
* @param {React.ForwardedRef<HTMLDivElement, RowProps>} ref
|
|
32
|
+
* @return {JSX.Element} Rendered rows with columns
|
|
33
|
+
*/
|
|
34
|
+
export const Row = forwardRef<HTMLDivElement, RowProps>((props: RowProps, ref) => {
|
|
35
|
+
const { isInsideEditor } = useContext<DotCMSPageContext | null>(
|
|
36
|
+
PageContext
|
|
37
|
+
) as DotCMSPageContext;
|
|
38
|
+
|
|
39
|
+
const { row } = props;
|
|
40
|
+
|
|
41
|
+
const combinedClasses = combineClasses([styles.row, row.styleClass]);
|
|
42
|
+
|
|
43
|
+
const rowProps = isInsideEditor ? { 'data-dot': 'row', 'data-testid': 'row', ref } : {};
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div {...rowProps} className={combinedClasses}>
|
|
47
|
+
{row.columns.map((column, index) => (
|
|
48
|
+
<Column key={index} column={column} />
|
|
49
|
+
))}
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
|
|
3
|
+
import { DotCMSPageContext } from '../models';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The `PageContext` is a React context that provides access to the DotCMS page context.
|
|
7
|
+
*
|
|
8
|
+
* @category Contexts
|
|
9
|
+
*/
|
|
10
|
+
export const PageContext = createContext<DotCMSPageContext | null>(null);
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react-hooks';
|
|
2
|
+
|
|
3
|
+
import * as sdkClient from '@dotcms/client';
|
|
4
|
+
|
|
5
|
+
import { useDotcmsEditor } from './useDotcmsEditor';
|
|
6
|
+
|
|
7
|
+
import { DotcmsPageProps } from '../components/DotcmsLayout/DotcmsLayout';
|
|
8
|
+
import { mockPageContext } from '../mocks/mockPageContext';
|
|
9
|
+
|
|
10
|
+
jest.mock('@dotcms/client', () => ({
|
|
11
|
+
...jest.requireActual('@dotcms/client'),
|
|
12
|
+
isInsideEditor: () => true,
|
|
13
|
+
postMessageToEditor: jest.fn(),
|
|
14
|
+
DotCmsClient: {
|
|
15
|
+
instance: {
|
|
16
|
+
editor: {
|
|
17
|
+
on: jest.fn(),
|
|
18
|
+
off: jest.fn(),
|
|
19
|
+
callbacks: {}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
const { DotCmsClient } = sdkClient as jest.Mocked<typeof sdkClient>;
|
|
26
|
+
|
|
27
|
+
describe('useDotcmsEditor', () => {
|
|
28
|
+
let isInsideEditorSpy: jest.SpyInstance<boolean>;
|
|
29
|
+
let initEditorSpy: jest.SpyInstance<void>;
|
|
30
|
+
let destroyEditorSpy: jest.SpyInstance<void>;
|
|
31
|
+
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
isInsideEditorSpy = jest.spyOn(sdkClient, 'isInsideEditor');
|
|
34
|
+
initEditorSpy = jest.spyOn(sdkClient, 'initEditor');
|
|
35
|
+
destroyEditorSpy = jest.spyOn(sdkClient, 'destroyEditor');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
afterEach(() => {
|
|
39
|
+
jest.clearAllMocks();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('when outside editor', () => {
|
|
43
|
+
it('should not call initEditor or destroyEditor when outside editor', () => {
|
|
44
|
+
isInsideEditorSpy.mockReturnValueOnce(false);
|
|
45
|
+
|
|
46
|
+
renderHook(() =>
|
|
47
|
+
useDotcmsEditor({
|
|
48
|
+
pageContext: mockPageContext,
|
|
49
|
+
config: { pathname: '' }
|
|
50
|
+
})
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
expect(initEditorSpy).not.toHaveBeenCalled();
|
|
54
|
+
expect(destroyEditorSpy).not.toHaveBeenCalled();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('when inside editor', () => {
|
|
59
|
+
it('should call initEditor when inside editor', () => {
|
|
60
|
+
isInsideEditorSpy.mockReturnValueOnce(true);
|
|
61
|
+
|
|
62
|
+
renderHook(() =>
|
|
63
|
+
useDotcmsEditor({
|
|
64
|
+
pageContext: mockPageContext,
|
|
65
|
+
config: { pathname: '' }
|
|
66
|
+
})
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
expect(initEditorSpy).toHaveBeenCalled();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should call destroyEditor on unmount when inside editor', () => {
|
|
73
|
+
isInsideEditorSpy.mockReturnValueOnce(true);
|
|
74
|
+
|
|
75
|
+
const { unmount } = renderHook(() =>
|
|
76
|
+
useDotcmsEditor({
|
|
77
|
+
pageContext: mockPageContext,
|
|
78
|
+
config: { pathname: '' }
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
unmount();
|
|
83
|
+
|
|
84
|
+
expect(destroyEditorSpy).toHaveBeenCalled();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('onReload', () => {
|
|
88
|
+
const dotCMSPagePropsMock = {
|
|
89
|
+
pageContext: mockPageContext,
|
|
90
|
+
config: {
|
|
91
|
+
pathname: '',
|
|
92
|
+
onReload: () => {
|
|
93
|
+
/* do nothing */
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
beforeEach(() => {
|
|
99
|
+
isInsideEditorSpy.mockReturnValueOnce(true);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should subscribe to the `CHANGE` event', () => {
|
|
103
|
+
const client = DotCmsClient.instance;
|
|
104
|
+
|
|
105
|
+
renderHook(() => useDotcmsEditor(dotCMSPagePropsMock));
|
|
106
|
+
|
|
107
|
+
expect(client.editor.on).toHaveBeenCalledWith('changes', expect.any(Function));
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should remove listener on unmount', () => {
|
|
111
|
+
const client = DotCmsClient.instance;
|
|
112
|
+
|
|
113
|
+
const { unmount } = renderHook(() => useDotcmsEditor(dotCMSPagePropsMock));
|
|
114
|
+
|
|
115
|
+
unmount();
|
|
116
|
+
|
|
117
|
+
expect(client.editor.off).toHaveBeenCalledWith('changes');
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('Client is ready', () => {
|
|
122
|
+
const dotCMSPagePropsMock = {
|
|
123
|
+
pageContext: mockPageContext,
|
|
124
|
+
config: {
|
|
125
|
+
pathname: '',
|
|
126
|
+
onReload: () => {
|
|
127
|
+
/* do nothing */
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
it('should send a message to the editor when the client is ready', () => {
|
|
133
|
+
const editor: sdkClient.EditorConfig = {
|
|
134
|
+
params: {
|
|
135
|
+
depth: '0'
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
renderHook(() =>
|
|
140
|
+
useDotcmsEditor({
|
|
141
|
+
...dotCMSPagePropsMock,
|
|
142
|
+
config: {
|
|
143
|
+
pathname: '',
|
|
144
|
+
editor
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
expect(sdkClient.postMessageToEditor).toHaveBeenCalledWith({
|
|
150
|
+
action: sdkClient.CUSTOMER_ACTIONS.CLIENT_READY,
|
|
151
|
+
payload: editor
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe('onChange', () => {
|
|
157
|
+
const dotCMSPagePropsMock = {
|
|
158
|
+
pageContext: mockPageContext,
|
|
159
|
+
config: {
|
|
160
|
+
pathname: '',
|
|
161
|
+
editor: {
|
|
162
|
+
query: '{query { asset { identifier } }}'
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
it('should update the page asset when changes are made in the editor', () => {
|
|
168
|
+
const client = DotCmsClient.instance;
|
|
169
|
+
|
|
170
|
+
renderHook(() => useDotcmsEditor(dotCMSPagePropsMock as DotcmsPageProps));
|
|
171
|
+
|
|
172
|
+
expect(client.editor.on).toHaveBeenCalledWith('changes', expect.any(Function));
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
CUSTOMER_ACTIONS,
|
|
5
|
+
DotCmsClient,
|
|
6
|
+
destroyEditor,
|
|
7
|
+
initEditor,
|
|
8
|
+
isInsideEditor as isInsideEditorFn,
|
|
9
|
+
postMessageToEditor,
|
|
10
|
+
updateNavigation
|
|
11
|
+
} from '@dotcms/client';
|
|
12
|
+
|
|
13
|
+
import { DotcmsPageProps } from '../components/DotcmsLayout/DotcmsLayout';
|
|
14
|
+
import { DotCMSPageContext } from '../models';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Custom Hook to handle the DotCMS editor interaction with the page.
|
|
18
|
+
*
|
|
19
|
+
* @category Hooks
|
|
20
|
+
* @param {DotcmsPageProps} props {
|
|
21
|
+
* pageContext,
|
|
22
|
+
* config,
|
|
23
|
+
* }
|
|
24
|
+
* @returns {DotCMSPageContext} The context for a DotCMS page provided by the editor.
|
|
25
|
+
*/
|
|
26
|
+
export const useDotcmsEditor = ({ pageContext, config }: DotcmsPageProps) => {
|
|
27
|
+
const { pathname, onReload, editor } = config;
|
|
28
|
+
const [state, setState] = useState<DotCMSPageContext>({
|
|
29
|
+
...pageContext,
|
|
30
|
+
isInsideEditor: false
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Initializes the DotCMS editor.
|
|
35
|
+
*/
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (!isInsideEditorFn()) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
initEditor({ pathname });
|
|
42
|
+
updateNavigation(pathname || '/');
|
|
43
|
+
setState((prevState) => ({ ...prevState, isInsideEditor: true }));
|
|
44
|
+
|
|
45
|
+
return () => destroyEditor();
|
|
46
|
+
}, [pathname]);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Reloads the page when changes are made in the editor.
|
|
50
|
+
*/
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
const insideEditor = isInsideEditorFn();
|
|
53
|
+
const client = DotCmsClient.instance;
|
|
54
|
+
|
|
55
|
+
if (!insideEditor || !onReload) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
client.editor.on('changes', () => onReload?.());
|
|
60
|
+
|
|
61
|
+
return () => client.editor.off('changes');
|
|
62
|
+
}, [onReload]);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Sends a message to the editor when the client is ready.
|
|
66
|
+
*/
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (!isInsideEditorFn()) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
postMessageToEditor({ action: CUSTOMER_ACTIONS.CLIENT_READY, payload: editor });
|
|
73
|
+
}, [pathname, editor]);
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Updates the page asset when changes are made in the editor.
|
|
77
|
+
*/
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (!isInsideEditorFn()) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const client = DotCmsClient.instance;
|
|
84
|
+
|
|
85
|
+
client.editor.on('changes', (data) => {
|
|
86
|
+
const pageAsset = data as DotCMSPageContext['pageAsset'];
|
|
87
|
+
setState((state) => ({ ...state, pageAsset }));
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return () => client.editor.off('changes');
|
|
91
|
+
}, []);
|
|
92
|
+
|
|
93
|
+
return state;
|
|
94
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react-hooks';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
import { useDotcmsPageContext } from './useDotcmsPageContext'; // Adjust the import path based on your file structure.
|
|
5
|
+
|
|
6
|
+
import { PageContext } from '../contexts/PageContext';
|
|
7
|
+
import { DotCMSPageContext } from '../models';
|
|
8
|
+
|
|
9
|
+
const mockContextValue: DotCMSPageContext = {
|
|
10
|
+
components: {},
|
|
11
|
+
isInsideEditor: false,
|
|
12
|
+
pageAsset: {
|
|
13
|
+
containers: {},
|
|
14
|
+
layout: {
|
|
15
|
+
header: false,
|
|
16
|
+
footer: false,
|
|
17
|
+
body: {
|
|
18
|
+
rows: []
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
page: {
|
|
22
|
+
title: 'Test Page',
|
|
23
|
+
identifier: 'test-page'
|
|
24
|
+
},
|
|
25
|
+
viewAs: {
|
|
26
|
+
language: {
|
|
27
|
+
id: 'en'
|
|
28
|
+
},
|
|
29
|
+
persona: {
|
|
30
|
+
keyTag: 'persona'
|
|
31
|
+
},
|
|
32
|
+
variantId: 'variant'
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
describe('useDotcmsPageContext', () => {
|
|
38
|
+
it('returns the context value', () => {
|
|
39
|
+
const { result } = renderHook(() => useDotcmsPageContext(), {
|
|
40
|
+
wrapper: ({ children }: { children: ReactNode }) => (
|
|
41
|
+
<PageContext.Provider value={mockContextValue}>{children}</PageContext.Provider>
|
|
42
|
+
)
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
expect(result.current).toEqual(mockContextValue);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
|
|
3
|
+
import { PageContext } from '../contexts/PageContext';
|
|
1
4
|
import { DotCMSPageContext } from '../models';
|
|
5
|
+
|
|
2
6
|
/**
|
|
3
7
|
* `useDotcmsPageContext` is a custom React hook that provides access to the `PageProviderContext`.
|
|
4
8
|
* It takes no parameters and returns the context value or `null` if it's not available.
|
|
@@ -6,4 +10,6 @@ import { DotCMSPageContext } from '../models';
|
|
|
6
10
|
* @category Hooks
|
|
7
11
|
* @returns {DotCMSPageContext | null} - The context value or `null` if it's not available.
|
|
8
12
|
*/
|
|
9
|
-
export
|
|
13
|
+
export function useDotcmsPageContext() {
|
|
14
|
+
return useContext<DotCMSPageContext | null>(PageContext);
|
|
15
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { PageProvider } from '../components/PageProvider/PageProvider';
|
|
2
|
+
import { DotCMSPageContext, DotCMSContentlet } from '../models';
|
|
3
|
+
|
|
4
|
+
export const dotcmsContentletMock: DotCMSContentlet = {
|
|
5
|
+
archived: false,
|
|
6
|
+
baseType: '',
|
|
7
|
+
contentType: '',
|
|
8
|
+
folder: '',
|
|
9
|
+
hasTitleImage: false,
|
|
10
|
+
host: '',
|
|
11
|
+
hostName: '',
|
|
12
|
+
identifier: '',
|
|
13
|
+
inode: '',
|
|
14
|
+
languageId: 1,
|
|
15
|
+
live: false,
|
|
16
|
+
locked: false,
|
|
17
|
+
modDate: '',
|
|
18
|
+
modUser: '',
|
|
19
|
+
modUserName: '',
|
|
20
|
+
owner: '',
|
|
21
|
+
sortOrder: 1,
|
|
22
|
+
stInode: '',
|
|
23
|
+
title: 'This is my editable title',
|
|
24
|
+
titleImage: '',
|
|
25
|
+
url: '',
|
|
26
|
+
working: false
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const mockPageContext: DotCMSPageContext = {
|
|
30
|
+
pageAsset: {
|
|
31
|
+
layout: {
|
|
32
|
+
header: true,
|
|
33
|
+
footer: true,
|
|
34
|
+
body: {
|
|
35
|
+
rows: [
|
|
36
|
+
{
|
|
37
|
+
styleClass: 'row',
|
|
38
|
+
columns: [
|
|
39
|
+
{
|
|
40
|
+
styleClass: 'col-md-12',
|
|
41
|
+
width: 6,
|
|
42
|
+
leftOffset: 3,
|
|
43
|
+
containers: [
|
|
44
|
+
{
|
|
45
|
+
identifier: 'container-1',
|
|
46
|
+
uuid: 'uuid-1'
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
containers: {
|
|
56
|
+
'container-1': {
|
|
57
|
+
container: {
|
|
58
|
+
path: 'path/to/container',
|
|
59
|
+
identifier: 'container-1',
|
|
60
|
+
maxContentlets: 100,
|
|
61
|
+
parentPermissionable: {}
|
|
62
|
+
},
|
|
63
|
+
containerStructures: [
|
|
64
|
+
{
|
|
65
|
+
contentTypeVar: 'content-type-1'
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
contentlets: {
|
|
69
|
+
'uuid-1': [
|
|
70
|
+
{
|
|
71
|
+
contentType: 'content-type-1',
|
|
72
|
+
identifier: 'contentlet-1',
|
|
73
|
+
title: 'Contentlet 1',
|
|
74
|
+
inode: 'inode-1',
|
|
75
|
+
onNumberOfPages: 1,
|
|
76
|
+
baseType: 'base-type-1'
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
'container-2': {
|
|
82
|
+
container: {
|
|
83
|
+
path: 'path/to/container',
|
|
84
|
+
identifier: 'container-2',
|
|
85
|
+
maxContentlets: 100,
|
|
86
|
+
parentPermissionable: {}
|
|
87
|
+
},
|
|
88
|
+
containerStructures: [
|
|
89
|
+
{
|
|
90
|
+
contentTypeVar: 'content-type-2'
|
|
91
|
+
}
|
|
92
|
+
],
|
|
93
|
+
contentlets: {
|
|
94
|
+
'uuid-2': []
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
page: { identifier: 'page-1', title: 'Hello Page' },
|
|
99
|
+
viewAs: { language: { id: 'en' }, persona: { keyTag: 'persona-1' }, variantId: 'variant-1' }
|
|
100
|
+
},
|
|
101
|
+
components: {},
|
|
102
|
+
isInsideEditor: false
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const MockContextRender = ({
|
|
106
|
+
children,
|
|
107
|
+
mockContext
|
|
108
|
+
}: {
|
|
109
|
+
children: JSX.Element;
|
|
110
|
+
mockContext: Partial<DotCMSPageContext>;
|
|
111
|
+
}) => {
|
|
112
|
+
return <PageProvider pageContext={mockContext}>{children}</PageProvider>;
|
|
113
|
+
};
|
|
@@ -1,8 +1,21 @@
|
|
|
1
|
-
/// <reference types="react" />
|
|
2
1
|
import { ContentNode } from './content-node.interface';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents a Block of content provided by the Block Editor
|
|
5
|
+
*
|
|
6
|
+
* @export
|
|
7
|
+
* @interface Block
|
|
8
|
+
*/
|
|
3
9
|
export interface Block {
|
|
4
10
|
content: ContentNode[];
|
|
5
11
|
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Props for a contentlet inside the Block Editor
|
|
15
|
+
*
|
|
16
|
+
* @export
|
|
17
|
+
* @interface DotContentletProps
|
|
18
|
+
*/
|
|
6
19
|
export interface DotContentletProps {
|
|
7
20
|
title: string;
|
|
8
21
|
baseType: string;
|
|
@@ -33,22 +46,36 @@ export interface DotContentletProps {
|
|
|
33
46
|
mimeType: string;
|
|
34
47
|
thumbnail?: string;
|
|
35
48
|
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Props for a block inside the Block Editor Component
|
|
52
|
+
*
|
|
53
|
+
* @export
|
|
54
|
+
* @interface BlockProps
|
|
55
|
+
*/
|
|
36
56
|
export interface BlockProps {
|
|
37
57
|
children: React.ReactNode;
|
|
38
58
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Represents the different types of Blocks that can be used in the Block Editor
|
|
62
|
+
*
|
|
63
|
+
* @export
|
|
64
|
+
* @enum {number}
|
|
65
|
+
*/
|
|
66
|
+
export enum Blocks {
|
|
67
|
+
PARAGRAPH = 'paragraph',
|
|
68
|
+
HEADING = 'heading',
|
|
69
|
+
TEXT = 'text',
|
|
70
|
+
BULLET_LIST = 'bulletList',
|
|
71
|
+
ORDERED_LIST = 'orderedList',
|
|
72
|
+
LIST_ITEM = 'listItem',
|
|
73
|
+
BLOCK_QUOTE = 'blockquote',
|
|
74
|
+
CODE_BLOCK = 'codeBlock',
|
|
75
|
+
HARDBREAK = 'hardBreak',
|
|
76
|
+
HORIZONTAL_RULE = 'horizontalRule',
|
|
77
|
+
DOT_IMAGE = 'dotImage',
|
|
78
|
+
DOT_VIDEO = 'dotVideo',
|
|
79
|
+
TABLE = 'table',
|
|
80
|
+
DOT_CONTENT = 'dotContent'
|
|
54
81
|
}
|