@elementor/editor-site-navigation 0.19.9 → 0.19.11
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 +23 -0
- package/dist/index.js +324 -177
- package/dist/index.mjs +309 -163
- package/package.json +4 -3
- package/src/api/recent-posts.ts +15 -0
- package/src/components/panel/actions-menu/action-menu-item.tsx +10 -13
- package/src/components/panel/actions-menu/actions/__tests__/delete.test.tsx +2 -2
- package/src/components/panel/actions-menu/actions/__tests__/set-home.test.tsx +4 -4
- package/src/components/panel/actions-menu/actions/__tests__/view.test.tsx +1 -1
- package/src/components/panel/actions-menu/actions/delete.tsx +8 -2
- package/src/components/panel/actions-menu/actions/duplicate.tsx +1 -1
- package/src/components/panel/actions-menu/actions/rename.tsx +1 -1
- package/src/components/panel/actions-menu/actions/set-home.tsx +12 -4
- package/src/components/panel/actions-menu/actions/view.tsx +1 -1
- package/src/components/panel/add-new-button.tsx +3 -1
- package/src/components/panel/error-snackbar.tsx +33 -0
- package/src/components/panel/posts-list/__tests__/post-list-item.test.tsx +2 -2
- package/src/components/panel/posts-list/__tests__/posts-collapsible-list.test.tsx +4 -4
- package/src/components/panel/posts-list/collapsible-list.tsx +22 -10
- package/src/components/panel/posts-list/error-state.tsx +37 -0
- package/src/components/panel/posts-list/list-items/edit-mode-template.tsx +48 -31
- package/src/components/panel/posts-list/list-items/list-item-create.tsx +13 -10
- package/src/components/panel/posts-list/list-items/list-item-duplicate.tsx +13 -10
- package/src/components/panel/posts-list/list-items/list-item-rename.tsx +25 -11
- package/src/components/panel/posts-list/list-items/list-item-view.tsx +8 -13
- package/src/components/panel/posts-list/posts-collapsible-list.tsx +31 -3
- package/src/components/panel/shell.tsx +5 -1
- package/src/components/shared/page-title-and-status.tsx +4 -3
- package/src/components/top-bar/__tests__/add-new-page.test.tsx +6 -5
- package/src/components/top-bar/__tests__/recently-edited.test.tsx +23 -11
- package/src/components/top-bar/post-list-item.tsx +2 -2
- package/src/components/top-bar/recently-edited.tsx +11 -1
- package/src/contexts/post-list-context.tsx +5 -0
- package/src/hooks/__tests__/use-recent-posts.test.ts +8 -9
- package/src/hooks/use-posts-actions.ts +2 -1
- package/src/hooks/use-recent-posts.ts +8 -47
- package/src/hooks/use-rename-active-document.ts +23 -0
- package/src/types.ts +12 -0
|
@@ -3,6 +3,8 @@ import { usePostListContext } from '../../../../contexts/post-list-context';
|
|
|
3
3
|
import { usePostActions } from '../../../../hooks/use-posts-actions';
|
|
4
4
|
import { Post } from '../../../../types';
|
|
5
5
|
import EditModeTemplate from './edit-mode-template';
|
|
6
|
+
import { __useActiveDocument as useActiveDocument } from '@elementor/editor-documents';
|
|
7
|
+
import useRenameActiveDocument from '../../../../hooks/use-rename-active-document';
|
|
6
8
|
|
|
7
9
|
type Props = {
|
|
8
10
|
post: Post,
|
|
@@ -11,23 +13,35 @@ type Props = {
|
|
|
11
13
|
export default function ListItemRename( { post }: Props ) {
|
|
12
14
|
const { type, resetEditMode } = usePostListContext();
|
|
13
15
|
const { updatePost } = usePostActions( type );
|
|
16
|
+
const { setError } = usePostListContext();
|
|
17
|
+
const activeDocument = useActiveDocument();
|
|
18
|
+
const rename = useRenameActiveDocument();
|
|
14
19
|
|
|
15
|
-
const
|
|
16
|
-
|
|
20
|
+
const isActive = activeDocument?.id === post.id;
|
|
21
|
+
const title = isActive ? activeDocument?.title : post.title.rendered;
|
|
22
|
+
|
|
23
|
+
const renamePostCallback = async ( inputValue: string ) => {
|
|
24
|
+
if ( inputValue === title ) {
|
|
17
25
|
resetEditMode();
|
|
18
26
|
}
|
|
19
27
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
try {
|
|
29
|
+
if ( isActive ) {
|
|
30
|
+
await rename( inputValue );
|
|
31
|
+
} else {
|
|
32
|
+
await updatePost.mutateAsync( {
|
|
33
|
+
id: post.id,
|
|
34
|
+
title: inputValue,
|
|
35
|
+
} );
|
|
36
|
+
}
|
|
37
|
+
} catch ( e ) {
|
|
38
|
+
setError();
|
|
39
|
+
} finally {
|
|
40
|
+
resetEditMode();
|
|
41
|
+
}
|
|
28
42
|
};
|
|
29
43
|
|
|
30
44
|
return (
|
|
31
|
-
<EditModeTemplate postTitle={
|
|
45
|
+
<EditModeTemplate postTitle={ title } isLoading={ updatePost.isPending } callback={ renamePostCallback } />
|
|
32
46
|
);
|
|
33
47
|
}
|
|
@@ -3,12 +3,11 @@ import {
|
|
|
3
3
|
bindMenu,
|
|
4
4
|
bindTrigger,
|
|
5
5
|
Divider,
|
|
6
|
+
IconButton,
|
|
6
7
|
ListItem,
|
|
7
8
|
ListItemButton,
|
|
8
|
-
ListItemIcon,
|
|
9
9
|
ListItemText,
|
|
10
10
|
Menu,
|
|
11
|
-
ToggleButton,
|
|
12
11
|
usePopupState,
|
|
13
12
|
} from '@elementor/ui';
|
|
14
13
|
import { DotsVerticalIcon, HomeIcon } from '@elementor/icons';
|
|
@@ -33,21 +32,21 @@ export default function ListItemView( { post }: { post: Post } ) {
|
|
|
33
32
|
} );
|
|
34
33
|
|
|
35
34
|
const isActive = activeDocument?.id === post.id;
|
|
35
|
+
const status = isActive ? activeDocument?.status.value : post.status;
|
|
36
|
+
const title = isActive ? activeDocument?.title : post.title.rendered;
|
|
36
37
|
|
|
37
38
|
return (
|
|
38
39
|
<>
|
|
39
40
|
<ListItem
|
|
40
41
|
disablePadding
|
|
41
42
|
secondaryAction={
|
|
42
|
-
<
|
|
43
|
+
<IconButton
|
|
43
44
|
value
|
|
44
|
-
color="secondary"
|
|
45
45
|
size="small"
|
|
46
|
-
selected={ popupState.isOpen }
|
|
47
46
|
{ ...bindTrigger( popupState ) }
|
|
48
47
|
>
|
|
49
48
|
<DotsVerticalIcon fontSize="small" />
|
|
50
|
-
</
|
|
49
|
+
</IconButton>
|
|
51
50
|
}
|
|
52
51
|
>
|
|
53
52
|
<ListItemButton
|
|
@@ -58,18 +57,14 @@ export default function ListItemView( { post }: { post: Post } ) {
|
|
|
58
57
|
}
|
|
59
58
|
} }
|
|
60
59
|
dense
|
|
61
|
-
disableGutters
|
|
62
60
|
>
|
|
63
|
-
<ListItemIcon />
|
|
64
61
|
<ListItemText
|
|
65
62
|
disableTypography={ true }
|
|
66
63
|
>
|
|
67
|
-
<PageTitleAndStatus
|
|
64
|
+
<PageTitleAndStatus title={ title } status={ status } />
|
|
68
65
|
</ListItemText>
|
|
69
66
|
{ post.isHome &&
|
|
70
|
-
|
|
71
|
-
<HomeIcon titleAccess={ __( 'Homepage', 'elementor' ) } color="disabled" />
|
|
72
|
-
</ListItemIcon>
|
|
67
|
+
<HomeIcon titleAccess={ __( 'Homepage', 'elementor' ) } color="disabled" />
|
|
73
68
|
}
|
|
74
69
|
</ListItemButton>
|
|
75
70
|
</ListItem>
|
|
@@ -83,7 +78,7 @@ export default function ListItemView( { post }: { post: Post } ) {
|
|
|
83
78
|
<Delete post={ post } />
|
|
84
79
|
<View post={ post } />
|
|
85
80
|
<Divider />
|
|
86
|
-
<SetHome post={ post } />
|
|
81
|
+
<SetHome post={ post } closeMenu={ () => popupState.close() } />
|
|
87
82
|
</Menu>
|
|
88
83
|
</>
|
|
89
84
|
);
|
|
@@ -8,6 +8,7 @@ import CollapsibleList from './collapsible-list';
|
|
|
8
8
|
import PostListItem from './post-list-item';
|
|
9
9
|
import { useHomepage } from '../../../hooks/use-homepage';
|
|
10
10
|
import AddNewButton from '../add-new-button';
|
|
11
|
+
import ErrorState from './error-state';
|
|
11
12
|
|
|
12
13
|
type Props = {
|
|
13
14
|
isOpenByDefault?: boolean,
|
|
@@ -15,9 +16,13 @@ type Props = {
|
|
|
15
16
|
|
|
16
17
|
export default function PostsCollapsibleList( { isOpenByDefault = false }: Props ) {
|
|
17
18
|
const { type, editMode } = usePostListContext();
|
|
18
|
-
const { data: posts, isLoading: postsLoading } = usePosts( type );
|
|
19
|
+
const { data: posts, isLoading: postsLoading, isError: postsError } = usePosts( type );
|
|
19
20
|
const { data: homepageSettings } = useHomepage();
|
|
20
21
|
|
|
22
|
+
if ( postsError ) {
|
|
23
|
+
return <ErrorState />;
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
if ( ! posts || postsLoading ) {
|
|
22
27
|
return (
|
|
23
28
|
<Box sx={ { px: 5 } }>
|
|
@@ -43,12 +48,36 @@ export default function PostsCollapsibleList( { isOpenByDefault = false }: Props
|
|
|
43
48
|
const isHomepageSet = homepageSettings?.show_on_front === 'page' && !! homepageSettings?.page_on_front;
|
|
44
49
|
const homepageId = isHomepageSet ? homepageSettings.page_on_front : null;
|
|
45
50
|
|
|
51
|
+
const mappedPosts = posts.map( ( post ) => {
|
|
52
|
+
if ( post.id === homepageId ) {
|
|
53
|
+
return { ...post, isHome: true };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return post;
|
|
57
|
+
} );
|
|
58
|
+
|
|
59
|
+
const sortedPosts = mappedPosts.sort( ( a, b ) => {
|
|
60
|
+
if ( a.id === homepageId ) {
|
|
61
|
+
return -1;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if ( b.id === homepageId ) {
|
|
65
|
+
return 1;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return 0;
|
|
69
|
+
} );
|
|
70
|
+
|
|
46
71
|
return (
|
|
47
72
|
<>
|
|
48
73
|
<Box
|
|
49
74
|
display="flex"
|
|
50
75
|
justifyContent="flex-end"
|
|
51
76
|
alignItems="center"
|
|
77
|
+
sx={ {
|
|
78
|
+
py: 1,
|
|
79
|
+
px: 2,
|
|
80
|
+
} }
|
|
52
81
|
>
|
|
53
82
|
<AddNewButton />
|
|
54
83
|
</Box>
|
|
@@ -58,8 +87,7 @@ export default function PostsCollapsibleList( { isOpenByDefault = false }: Props
|
|
|
58
87
|
Icon={ PageTypeIcon }
|
|
59
88
|
isOpenByDefault={ isOpenByDefault || false }
|
|
60
89
|
>
|
|
61
|
-
{
|
|
62
|
-
post = { ...post, isHome: post.id === homepageId };
|
|
90
|
+
{ sortedPosts.map( ( post ) => {
|
|
63
91
|
return <PostListItem key={ post.id } post={ post } />;
|
|
64
92
|
} ) }
|
|
65
93
|
{
|
|
@@ -3,17 +3,21 @@ import { Panel, PanelBody, PanelHeader, PanelHeaderTitle } from '@elementor/edit
|
|
|
3
3
|
import { __ } from '@wordpress/i18n';
|
|
4
4
|
import PostsCollapsibleList from './posts-list/posts-collapsible-list';
|
|
5
5
|
import { PostListContextProvider } from '../../contexts/post-list-context';
|
|
6
|
+
import ErrorSnackbar from './error-snackbar';
|
|
6
7
|
|
|
7
8
|
const Shell = () => {
|
|
9
|
+
const [ isErrorSnackbarOpen, setIsErrorSnackbarOpen ] = React.useState( false );
|
|
10
|
+
|
|
8
11
|
return (
|
|
9
12
|
<Panel>
|
|
10
13
|
<PanelHeader>
|
|
11
14
|
<PanelHeaderTitle>{ __( 'Pages', 'elementor' ) }</PanelHeaderTitle>
|
|
12
15
|
</PanelHeader>
|
|
13
16
|
<PanelBody>
|
|
14
|
-
<PostListContextProvider type={ 'page' }>
|
|
17
|
+
<PostListContextProvider type={ 'page' } setError={ () => setIsErrorSnackbarOpen( true ) }>
|
|
15
18
|
<PostsCollapsibleList isOpenByDefault={ true } />
|
|
16
19
|
</PostListContextProvider>
|
|
20
|
+
<ErrorSnackbar open={ isErrorSnackbarOpen } onClose={ () => setIsErrorSnackbarOpen( false ) } />
|
|
17
21
|
</PanelBody>
|
|
18
22
|
</Panel>
|
|
19
23
|
);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { Box, Typography } from '@elementor/ui';
|
|
3
|
-
import { Post } from '../../types';
|
|
4
3
|
import { useReverseHtmlEntities } from '../../hooks/use-reverse-html-entities';
|
|
5
4
|
|
|
6
5
|
const PageStatus = ( { status }: { status: string } ) => {
|
|
@@ -12,6 +11,7 @@ const PageStatus = ( { status }: { status: string } ) => {
|
|
|
12
11
|
<Typography
|
|
13
12
|
component="span"
|
|
14
13
|
variant="body2"
|
|
14
|
+
color="text.secondary"
|
|
15
15
|
sx={ {
|
|
16
16
|
textTransform: 'capitalize',
|
|
17
17
|
fontStyle: 'italic',
|
|
@@ -31,6 +31,7 @@ const PageTitle = ( { title }: { title: string } ) => {
|
|
|
31
31
|
<Typography
|
|
32
32
|
component="span"
|
|
33
33
|
variant="body2"
|
|
34
|
+
color="text.secondary"
|
|
34
35
|
noWrap
|
|
35
36
|
sx={ {
|
|
36
37
|
flexBasis: 'auto',
|
|
@@ -41,10 +42,10 @@ const PageTitle = ( { title }: { title: string } ) => {
|
|
|
41
42
|
);
|
|
42
43
|
};
|
|
43
44
|
|
|
44
|
-
export default function PageTitleAndStatus( {
|
|
45
|
+
export default function PageTitleAndStatus( { title, status }: { title: string, status: string } ) {
|
|
45
46
|
return (
|
|
46
47
|
<Box display="flex">
|
|
47
|
-
<PageTitle title={
|
|
48
|
+
<PageTitle title={ title } /> <PageStatus status={ status } />
|
|
48
49
|
</Box>
|
|
49
50
|
);
|
|
50
51
|
}
|
|
@@ -3,8 +3,9 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
|
3
3
|
import { __useActiveDocument as useActiveDocument, __useNavigateToDocument as useNavigateToDocument } from '@elementor/editor-documents';
|
|
4
4
|
import RecentlyEdited from '../recently-edited';
|
|
5
5
|
import { createMockDocument } from 'test-utils';
|
|
6
|
-
import useRecentPosts
|
|
6
|
+
import useRecentPosts from '../../../hooks/use-recent-posts';
|
|
7
7
|
import useCreatePage from '../../../hooks/use-create-page';
|
|
8
|
+
import { RecentPost } from '../../../types';
|
|
8
9
|
|
|
9
10
|
jest.mock( '@elementor/editor-documents', () => ( {
|
|
10
11
|
__useActiveDocument: jest.fn(),
|
|
@@ -30,9 +31,9 @@ describe( '@elementor/recently-edited - Top bar add new page', () => {
|
|
|
30
31
|
it( 'should render add new page button', () => {
|
|
31
32
|
// Arrange.
|
|
32
33
|
const isLoading = false;
|
|
33
|
-
const recentPosts:
|
|
34
|
+
const recentPosts: RecentPost[] = [];
|
|
34
35
|
|
|
35
|
-
jest.mocked( useRecentPosts ).mockReturnValue( { isLoading, recentPosts } );
|
|
36
|
+
jest.mocked( useRecentPosts ).mockReturnValue( { isLoading, data: recentPosts } as ReturnType<typeof useRecentPosts> );
|
|
36
37
|
|
|
37
38
|
render( <RecentlyEdited /> );
|
|
38
39
|
|
|
@@ -48,11 +49,11 @@ describe( '@elementor/recently-edited - Top bar add new page', () => {
|
|
|
48
49
|
it( 'should trigger create page hook on click', async () => {
|
|
49
50
|
// Arrange.
|
|
50
51
|
const isLoading = false;
|
|
51
|
-
const recentPosts:
|
|
52
|
+
const recentPosts: RecentPost[] = [];
|
|
52
53
|
const create = jest.fn().mockReturnValue( Promise.resolve( { id: 123 } ) );
|
|
53
54
|
const navigateToDocument = jest.fn();
|
|
54
55
|
|
|
55
|
-
jest.mocked( useRecentPosts ).mockReturnValue( { isLoading, recentPosts } );
|
|
56
|
+
jest.mocked( useRecentPosts ).mockReturnValue( { isLoading, data: recentPosts } as ReturnType<typeof useRecentPosts> );
|
|
56
57
|
jest.mocked( useCreatePage ).mockReturnValue( { isLoading, create } );
|
|
57
58
|
jest.mocked( useNavigateToDocument ).mockReturnValue( navigateToDocument );
|
|
58
59
|
|
|
@@ -3,7 +3,8 @@ import { fireEvent, render, screen } from '@testing-library/react';
|
|
|
3
3
|
import { __useHostDocument as useHostDocument, __useActiveDocument as useActiveDocument, __useNavigateToDocument as useNavigateToDocument } from '@elementor/editor-documents';
|
|
4
4
|
import RecentlyEdited from '../recently-edited';
|
|
5
5
|
import { createMockDocument } from 'test-utils';
|
|
6
|
-
import useRecentPosts
|
|
6
|
+
import useRecentPosts from '../../../hooks/use-recent-posts';
|
|
7
|
+
import { RecentPost } from '../../../types';
|
|
7
8
|
|
|
8
9
|
jest.mock( '@elementor/editor-documents', () => ( {
|
|
9
10
|
__useActiveDocument: jest.fn(),
|
|
@@ -13,7 +14,7 @@ jest.mock( '@elementor/editor-documents', () => ( {
|
|
|
13
14
|
|
|
14
15
|
jest.mock( '../../../hooks/use-recent-posts', () => (
|
|
15
16
|
{
|
|
16
|
-
default: jest.fn( () => ( { isLoading: false,
|
|
17
|
+
default: jest.fn( () => ( { isLoading: false, data: [] } ) ),
|
|
17
18
|
__esModule: true,
|
|
18
19
|
}
|
|
19
20
|
) );
|
|
@@ -117,9 +118,9 @@ describe( '@elementor/recently-edited - Top bar Recently Edited', () => {
|
|
|
117
118
|
} ) );
|
|
118
119
|
|
|
119
120
|
const isLoading = false;
|
|
120
|
-
const recentPosts:
|
|
121
|
+
const recentPosts: RecentPost[] = [];
|
|
121
122
|
|
|
122
|
-
jest.mocked( useRecentPosts ).mockReturnValue( { isLoading, recentPosts } );
|
|
123
|
+
jest.mocked( useRecentPosts ).mockReturnValue( { isLoading, data: recentPosts } as ReturnType<typeof useRecentPosts> );
|
|
123
124
|
|
|
124
125
|
render( <RecentlyEdited /> );
|
|
125
126
|
|
|
@@ -144,8 +145,8 @@ describe( '@elementor/recently-edited - Top bar Recently Edited', () => {
|
|
|
144
145
|
} ) );
|
|
145
146
|
|
|
146
147
|
const isLoading = false;
|
|
147
|
-
const recentPosts:
|
|
148
|
-
id:
|
|
148
|
+
const recentPosts: RecentPost[] = [ {
|
|
149
|
+
id: 2,
|
|
149
150
|
title: 'Test post',
|
|
150
151
|
edit_url: 'some_url',
|
|
151
152
|
type: {
|
|
@@ -156,7 +157,7 @@ describe( '@elementor/recently-edited - Top bar Recently Edited', () => {
|
|
|
156
157
|
date_modified: 123,
|
|
157
158
|
} ];
|
|
158
159
|
|
|
159
|
-
jest.mocked( useRecentPosts ).mockReturnValue( { isLoading, recentPosts } );
|
|
160
|
+
jest.mocked( useRecentPosts ).mockReturnValue( { isLoading, data: recentPosts } as ReturnType<typeof useRecentPosts> );
|
|
160
161
|
|
|
161
162
|
render( <RecentlyEdited /> );
|
|
162
163
|
|
|
@@ -186,9 +187,20 @@ describe( '@elementor/recently-edited - Top bar Recently Edited', () => {
|
|
|
186
187
|
} ) );
|
|
187
188
|
|
|
188
189
|
const isLoading = false;
|
|
189
|
-
const recentPosts:
|
|
190
|
+
const recentPosts: RecentPost[] = [
|
|
190
191
|
{
|
|
191
192
|
id: 1,
|
|
193
|
+
title: 'Header title with special char ¥',
|
|
194
|
+
edit_url: 'some_url',
|
|
195
|
+
type: {
|
|
196
|
+
post_type: 'post',
|
|
197
|
+
doc_type: 'wp-post',
|
|
198
|
+
label: 'Post',
|
|
199
|
+
},
|
|
200
|
+
date_modified: 123,
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
id: 3,
|
|
192
204
|
title: 'Post title with <h1>HTML</h1>',
|
|
193
205
|
edit_url: 'some_url',
|
|
194
206
|
type: {
|
|
@@ -211,7 +223,7 @@ describe( '@elementor/recently-edited - Top bar Recently Edited', () => {
|
|
|
211
223
|
},
|
|
212
224
|
];
|
|
213
225
|
|
|
214
|
-
jest.mocked( useRecentPosts ).mockReturnValue( { isLoading, recentPosts } );
|
|
226
|
+
jest.mocked( useRecentPosts ).mockReturnValue( { isLoading, data: recentPosts } as ReturnType<typeof useRecentPosts> );
|
|
215
227
|
|
|
216
228
|
// Act.
|
|
217
229
|
render( <RecentlyEdited /> );
|
|
@@ -235,7 +247,7 @@ describe( '@elementor/recently-edited - Top bar Recently Edited', () => {
|
|
|
235
247
|
|
|
236
248
|
jest.mocked( useRecentPosts ).mockReturnValue( {
|
|
237
249
|
isLoading: false,
|
|
238
|
-
|
|
250
|
+
data: [ {
|
|
239
251
|
id: 123,
|
|
240
252
|
title: 'Test post',
|
|
241
253
|
edit_url: 'some_url',
|
|
@@ -246,7 +258,7 @@ describe( '@elementor/recently-edited - Top bar Recently Edited', () => {
|
|
|
246
258
|
},
|
|
247
259
|
date_modified: 123,
|
|
248
260
|
} ],
|
|
249
|
-
} );
|
|
261
|
+
} as ReturnType<typeof useRecentPosts> );
|
|
250
262
|
|
|
251
263
|
render( <RecentlyEdited /> );
|
|
252
264
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import DocTypeChip from './chip-doc-type';
|
|
2
2
|
import { MenuItem, MenuItemProps, ListItemText } from '@elementor/ui';
|
|
3
3
|
import * as React from 'react';
|
|
4
|
-
import { Post } from '../../hooks/use-recent-posts';
|
|
5
4
|
import { __useNavigateToDocument as useNavigateToDocument } from '@elementor/editor-documents';
|
|
6
5
|
import { useReverseHtmlEntities } from '../../hooks/use-reverse-html-entities';
|
|
6
|
+
import { RecentPost } from '../../types';
|
|
7
7
|
|
|
8
8
|
type Props = MenuItemProps & {
|
|
9
|
-
post:
|
|
9
|
+
post: RecentPost;
|
|
10
10
|
closePopup: () => void;
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -18,6 +18,7 @@ import { __ } from '@wordpress/i18n';
|
|
|
18
18
|
import { PostListItem } from './post-list-item';
|
|
19
19
|
import { CreatePostListItem } from './create-post-list-item';
|
|
20
20
|
import { useReverseHtmlEntities } from '../../hooks/use-reverse-html-entities';
|
|
21
|
+
import { NUMBER_OF_RECENT_POSTS } from '../../api/recent-posts';
|
|
21
22
|
|
|
22
23
|
export default function RecentlyEdited() {
|
|
23
24
|
const activeDocument = useActiveDocument();
|
|
@@ -26,7 +27,16 @@ export default function RecentlyEdited() {
|
|
|
26
27
|
? activeDocument
|
|
27
28
|
: hostDocument;
|
|
28
29
|
|
|
29
|
-
const {
|
|
30
|
+
const { data } = useRecentPosts();
|
|
31
|
+
|
|
32
|
+
const getRecentPosts = () => {
|
|
33
|
+
if ( ! data ) {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return data.filter( ( post ) => post.id !== document?.id ).splice( 0, NUMBER_OF_RECENT_POSTS - 1 );
|
|
38
|
+
};
|
|
39
|
+
const recentPosts = getRecentPosts();
|
|
30
40
|
|
|
31
41
|
const popupState = usePopupState( {
|
|
32
42
|
variant: 'popover',
|
|
@@ -26,6 +26,7 @@ type ContextType = {
|
|
|
26
26
|
editMode: EditMode,
|
|
27
27
|
setEditMode: Dispatch<SetStateAction<EditMode>>,
|
|
28
28
|
resetEditMode: () => void,
|
|
29
|
+
setError: () => void,
|
|
29
30
|
};
|
|
30
31
|
|
|
31
32
|
const defaultValues: ContextType = {
|
|
@@ -33,15 +34,18 @@ const defaultValues: ContextType = {
|
|
|
33
34
|
editMode: { mode: 'none', details: {} },
|
|
34
35
|
setEditMode: () => null,
|
|
35
36
|
resetEditMode: () => null,
|
|
37
|
+
setError: () => null,
|
|
36
38
|
};
|
|
37
39
|
|
|
38
40
|
export const PostListContext = createContext<ContextType>( defaultValues );
|
|
39
41
|
|
|
40
42
|
export const PostListContextProvider = ( {
|
|
41
43
|
type,
|
|
44
|
+
setError,
|
|
42
45
|
children,
|
|
43
46
|
}: {
|
|
44
47
|
type: ContextType[ 'type' ],
|
|
48
|
+
setError: ContextType[ 'setError' ],
|
|
45
49
|
children: ReactNode,
|
|
46
50
|
} ) => {
|
|
47
51
|
const [ editMode, setEditMode ] = useState( defaultValues.editMode );
|
|
@@ -56,6 +60,7 @@ export const PostListContextProvider = ( {
|
|
|
56
60
|
editMode,
|
|
57
61
|
setEditMode,
|
|
58
62
|
resetEditMode,
|
|
63
|
+
setError,
|
|
59
64
|
} }>
|
|
60
65
|
{ children }
|
|
61
66
|
</PostListContext.Provider>
|
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import { waitFor
|
|
1
|
+
import { waitFor } from '@testing-library/react';
|
|
2
2
|
import apiFetch from '@wordpress/api-fetch';
|
|
3
|
-
import useRecentPosts
|
|
3
|
+
import useRecentPosts from '../use-recent-posts';
|
|
4
|
+
import { renderHookWithQuery } from 'test-utils';
|
|
5
|
+
import { baseUrl } from '../../api/recent-posts';
|
|
4
6
|
|
|
5
7
|
// Mock apiFetch to return a promise that resolves to an empty array.
|
|
6
8
|
jest.mock( '@wordpress/api-fetch' );
|
|
7
9
|
|
|
8
10
|
describe( 'useRecentPosts', () => {
|
|
9
|
-
beforeEach( () => {
|
|
10
|
-
jest.mocked( apiFetch ).mockImplementation( () => Promise.resolve( [] ) );
|
|
11
|
-
} );
|
|
12
|
-
|
|
13
11
|
afterEach( () => {
|
|
14
12
|
jest.clearAllMocks();
|
|
15
13
|
} );
|
|
@@ -28,14 +26,15 @@ describe( 'useRecentPosts', () => {
|
|
|
28
26
|
|
|
29
27
|
jest.mocked( apiFetch ).mockImplementation( () => Promise.resolve( posts ) );
|
|
30
28
|
|
|
31
|
-
|
|
29
|
+
// Act.
|
|
30
|
+
const { component } = renderHookWithQuery( () => useRecentPosts() );
|
|
32
31
|
|
|
33
32
|
expect( apiFetch ).toHaveBeenCalledWith( {
|
|
34
|
-
path:
|
|
33
|
+
path: `${ baseUrl }?posts_per_page=6`,
|
|
35
34
|
} );
|
|
36
35
|
|
|
37
36
|
await waitFor( () => {
|
|
38
|
-
expect( result.current.
|
|
37
|
+
expect( component.result.current.data ).toBe( posts );
|
|
39
38
|
} );
|
|
40
39
|
} );
|
|
41
40
|
} );
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useQueryClient, useMutation } from '@elementor/query';
|
|
2
2
|
import { createRequest, deleteRequest, updateRequest, duplicateRequest, NewPost, Slug, UpdatePost } from '../api/post';
|
|
3
3
|
import { postsQueryKey } from './use-posts';
|
|
4
|
+
import { recentPostsQueryKey } from './use-recent-posts';
|
|
4
5
|
|
|
5
6
|
export function usePostActions( postTypeSlug: Slug ) {
|
|
6
7
|
const invalidatePosts = useInvalidatePosts( postTypeSlug );
|
|
@@ -40,7 +41,7 @@ function useInvalidatePosts( postTypeSlug: string ) {
|
|
|
40
41
|
|
|
41
42
|
return ( options = {} ) => {
|
|
42
43
|
const queryKey = postsQueryKey( postTypeSlug );
|
|
43
|
-
|
|
44
|
+
queryClient.invalidateQueries( { queryKey: recentPostsQueryKey }, options );
|
|
44
45
|
return queryClient.invalidateQueries( { queryKey }, options );
|
|
45
46
|
};
|
|
46
47
|
}
|
|
@@ -1,50 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import { addQueryArgs } from '@wordpress/url';
|
|
1
|
+
import { useQuery } from '@elementor/query';
|
|
2
|
+
import { getRequest } from '../api/recent-posts';
|
|
4
3
|
|
|
5
|
-
export
|
|
6
|
-
id: number,
|
|
7
|
-
title: string,
|
|
8
|
-
edit_url: string,
|
|
9
|
-
type: {
|
|
10
|
-
post_type: string,
|
|
11
|
-
doc_type: string,
|
|
12
|
-
label: string,
|
|
13
|
-
},
|
|
14
|
-
date_modified: number,
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const endpointPath = '/elementor/v1/site-navigation/recent-posts';
|
|
18
|
-
|
|
19
|
-
export default function useRecentPosts( documentId?: number ) {
|
|
20
|
-
const [ recentPosts, setRecentPosts ] = useState<Post[]>( [] );
|
|
21
|
-
const [ isLoading, setIsLoading ] = useState( false );
|
|
22
|
-
|
|
23
|
-
useEffect( () => {
|
|
24
|
-
if ( documentId ) {
|
|
25
|
-
setIsLoading( true );
|
|
26
|
-
|
|
27
|
-
fetchRecentlyEditedPosts( documentId ).then( ( posts ) => {
|
|
28
|
-
setRecentPosts( posts );
|
|
29
|
-
setIsLoading( false );
|
|
30
|
-
} );
|
|
31
|
-
}
|
|
32
|
-
}, [ documentId ] );
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
isLoading,
|
|
36
|
-
recentPosts,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function fetchRecentlyEditedPosts( documentId: number ) {
|
|
41
|
-
const queryParams = {
|
|
42
|
-
posts_per_page: 5,
|
|
43
|
-
post__not_in: documentId,
|
|
44
|
-
};
|
|
4
|
+
export const recentPostsQueryKey = [ 'site-navigation', 'recent-posts' ];
|
|
45
5
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
6
|
+
export default function useRecentPosts() {
|
|
7
|
+
return useQuery( {
|
|
8
|
+
queryKey: recentPostsQueryKey,
|
|
9
|
+
queryFn: () => getRequest(),
|
|
10
|
+
} );
|
|
50
11
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { __privateRunCommand as runCommand } from '@elementor/editor-v1-adapters';
|
|
2
|
+
import { ExtendedWindow } from '@elementor/editor-documents';
|
|
3
|
+
|
|
4
|
+
function getV1DocumentsManager() {
|
|
5
|
+
const documentsManager = ( window as unknown as ExtendedWindow ).elementor?.documents;
|
|
6
|
+
|
|
7
|
+
if ( ! documentsManager ) {
|
|
8
|
+
throw new Error( 'Elementor Editor V1 documents manager not found' );
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return documentsManager;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default function useRenameActiveDocument() {
|
|
15
|
+
return async ( title: string ) => {
|
|
16
|
+
const currentDocument = getV1DocumentsManager().getCurrent();
|
|
17
|
+
const container = currentDocument.container;
|
|
18
|
+
await runCommand( 'document/elements/settings', {
|
|
19
|
+
container,
|
|
20
|
+
settings: { post_title: title },
|
|
21
|
+
} );
|
|
22
|
+
};
|
|
23
|
+
}
|
package/src/types.ts
CHANGED