@elementor/editor-site-navigation 0.15.0 → 0.17.0
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 +22 -0
- package/dist/index.js +194 -101
- package/dist/index.mjs +193 -91
- package/package.json +2 -2
- package/src/api/settings.ts +28 -0
- package/src/components/panel/actions-menu/action-menu-item.tsx +19 -4
- package/src/components/panel/actions-menu/actions/__tests__/delete.test.tsx +82 -0
- package/src/components/panel/actions-menu/actions/__tests__/set-home.test.tsx +67 -0
- package/src/components/panel/actions-menu/actions/delete.tsx +82 -9
- package/src/components/panel/actions-menu/actions/duplicate.tsx +17 -11
- package/src/components/panel/actions-menu/actions/rename.tsx +11 -7
- package/src/components/panel/actions-menu/actions/set-home.tsx +15 -3
- package/src/components/panel/actions-menu/actions/view.tsx +5 -3
- package/src/components/panel/posts-list/__tests__/posts-collapsible-list.test.tsx +25 -10
- package/src/components/panel/posts-list/list-items/list-item-view.tsx +2 -1
- package/src/components/panel/posts-list/posts-collapsible-list.tsx +9 -1
- package/src/hooks/__tests__/use-homepage-actions.test.ts +43 -0
- package/src/hooks/__tests__/use-homepage.test.ts +42 -0
- package/src/hooks/use-homepage-actions.ts +26 -0
- package/src/hooks/use-homepage.ts +11 -0
- package/src/types.ts +0 -10
- package/src/components/panel/actions-menu/action-list-item.tsx +0 -27
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { fireEvent, render, screen } from '@testing-library/react';
|
|
2
|
+
import SetHome from '../set-home';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { Post } from '../../../../../types';
|
|
5
|
+
|
|
6
|
+
const mockMutateAsync = jest.fn();
|
|
7
|
+
jest.mock( '../../../../../hooks/use-homepage-actions', () => ( {
|
|
8
|
+
__esModule: true,
|
|
9
|
+
useHomepageActions: jest.fn( () => ( {
|
|
10
|
+
updateSettingsMutation: {
|
|
11
|
+
mutateAsync: mockMutateAsync,
|
|
12
|
+
isLoading: false,
|
|
13
|
+
},
|
|
14
|
+
} ) ),
|
|
15
|
+
} ) );
|
|
16
|
+
|
|
17
|
+
describe( '@elementor/editor-site-navigation - SetHome', () => {
|
|
18
|
+
afterAll( () => {
|
|
19
|
+
jest.clearAllMocks();
|
|
20
|
+
} );
|
|
21
|
+
|
|
22
|
+
it( 'should render Set as homepage', () => {
|
|
23
|
+
// Arrange.
|
|
24
|
+
const post: Post = {
|
|
25
|
+
id: 1,
|
|
26
|
+
title: {
|
|
27
|
+
rendered: 'Test Page',
|
|
28
|
+
},
|
|
29
|
+
status: 'publish',
|
|
30
|
+
type: 'page',
|
|
31
|
+
link: 'https://example.local/test-page',
|
|
32
|
+
isHome: false,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Act.
|
|
36
|
+
render( <SetHome post={ post } /> );
|
|
37
|
+
|
|
38
|
+
// Assert.
|
|
39
|
+
const button = screen.getByRole( 'button' );
|
|
40
|
+
expect( button ).not.toHaveAttribute( 'aria-disabled' );
|
|
41
|
+
|
|
42
|
+
fireEvent.click( button );
|
|
43
|
+
|
|
44
|
+
expect( mockMutateAsync ).toHaveBeenCalledTimes( 1 );
|
|
45
|
+
} );
|
|
46
|
+
|
|
47
|
+
it( 'should render Set as homepage disabled when the page status is draft', () => {
|
|
48
|
+
// Arrange.
|
|
49
|
+
const post: Post = {
|
|
50
|
+
id: 1,
|
|
51
|
+
title: {
|
|
52
|
+
rendered: 'Test Page',
|
|
53
|
+
},
|
|
54
|
+
status: 'draft',
|
|
55
|
+
type: 'page',
|
|
56
|
+
link: 'https://example.local/test-page',
|
|
57
|
+
isHome: false,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Act.
|
|
61
|
+
render( <SetHome post={ post } /> );
|
|
62
|
+
|
|
63
|
+
// Assert.
|
|
64
|
+
const button = screen.getByRole( 'button' );
|
|
65
|
+
expect( button ).toHaveAttribute( 'aria-disabled', 'true' );
|
|
66
|
+
} );
|
|
67
|
+
} );
|
|
@@ -1,21 +1,94 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { TrashIcon } from '@elementor/icons';
|
|
3
3
|
import { Post } from '../../../../types';
|
|
4
|
-
import {
|
|
5
|
-
import { __ } from '@wordpress/i18n';
|
|
4
|
+
import { __, sprintf } from '@wordpress/i18n';
|
|
6
5
|
import ActionMenuItem from '../action-menu-item';
|
|
6
|
+
import { usePostActions } from '../../../../hooks/use-posts-actions';
|
|
7
|
+
import {
|
|
8
|
+
Button,
|
|
9
|
+
CircularProgress,
|
|
10
|
+
Dialog,
|
|
11
|
+
DialogActions,
|
|
12
|
+
DialogContent,
|
|
13
|
+
DialogContentText,
|
|
14
|
+
DialogTitle,
|
|
15
|
+
Divider,
|
|
16
|
+
} from '@elementor/ui';
|
|
17
|
+
import { usePostListContext } from '../../../../contexts/post-list-context';
|
|
18
|
+
import { useState } from 'react';
|
|
19
|
+
import { useActiveDocument } from '@elementor/editor-documents';
|
|
7
20
|
|
|
8
21
|
export default function Delete( { post }: { post: Post } ) {
|
|
22
|
+
const [ isDialogOpen, setIsDialogOpen ] = useState( false );
|
|
9
23
|
const activeDocument = useActiveDocument();
|
|
10
24
|
|
|
11
|
-
const
|
|
25
|
+
const isPostActive = activeDocument?.id === post.id;
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<>
|
|
29
|
+
<ActionMenuItem
|
|
30
|
+
title={ __( 'Delete', 'elementor' ) }
|
|
31
|
+
icon={ TrashIcon }
|
|
32
|
+
ListItemButtonProps={
|
|
33
|
+
{
|
|
34
|
+
disabled: post.isHome || isPostActive,
|
|
35
|
+
onClick: () => setIsDialogOpen( true ),
|
|
36
|
+
sx: { '&:hover': { color: 'error.main' } },
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/>
|
|
40
|
+
|
|
41
|
+
{
|
|
42
|
+
isDialogOpen && (
|
|
43
|
+
<DeleteDialog post={ post } setIsDialogOpen={ setIsDialogOpen } />
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
</>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function DeleteDialog( { post, setIsDialogOpen }: { post: Post, setIsDialogOpen: React.Dispatch<React.SetStateAction<boolean>> } ) {
|
|
51
|
+
const { type } = usePostListContext();
|
|
52
|
+
const { deletePost } = usePostActions( type );
|
|
53
|
+
|
|
54
|
+
/* translators: %s: Post title. */
|
|
55
|
+
const dialogTitle = sprintf( __( 'Delete “%s”?', 'elementor' ), post.title.rendered );
|
|
56
|
+
|
|
57
|
+
const deletePage = async () => {
|
|
58
|
+
await deletePost.mutateAsync( post.id );
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const handleCancel = () => {
|
|
62
|
+
if ( deletePost.isLoading ) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
setIsDialogOpen( false );
|
|
67
|
+
};
|
|
12
68
|
|
|
13
69
|
return (
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
70
|
+
<Dialog
|
|
71
|
+
open={ true }
|
|
72
|
+
onClose={ handleCancel }
|
|
73
|
+
aria-labelledby="delete-dialog"
|
|
74
|
+
>
|
|
75
|
+
<DialogTitle>
|
|
76
|
+
{ dialogTitle }
|
|
77
|
+
</DialogTitle>
|
|
78
|
+
<Divider />
|
|
79
|
+
<DialogContent>
|
|
80
|
+
<DialogContentText>
|
|
81
|
+
{ __( 'The page and its content will be deleted forever and we won’t be able to recover them.', 'elementor' ) }
|
|
82
|
+
</DialogContentText>
|
|
83
|
+
</DialogContent>
|
|
84
|
+
<DialogActions>
|
|
85
|
+
<Button variant="contained" color="secondary" onClick={ handleCancel } disabled={ deletePost.isLoading }>
|
|
86
|
+
{ __( 'Cancel', 'elementor' ) }
|
|
87
|
+
</Button>
|
|
88
|
+
<Button variant="contained" color="error" onClick={ deletePage } disabled={ deletePost.isLoading } >
|
|
89
|
+
{ ! deletePost.isLoading ? __( 'Delete', 'elementor' ) : <CircularProgress /> }
|
|
90
|
+
</Button>
|
|
91
|
+
</DialogActions>
|
|
92
|
+
</Dialog>
|
|
20
93
|
);
|
|
21
94
|
}
|
|
@@ -9,21 +9,27 @@ import { Post } from '../../../../types';
|
|
|
9
9
|
export default function Duplicate( { post, popupState }: { post: Post, popupState: PopupState } ) {
|
|
10
10
|
const { setEditMode } = usePostListContext();
|
|
11
11
|
|
|
12
|
+
const onClick = () => {
|
|
13
|
+
popupState.close();
|
|
14
|
+
|
|
15
|
+
setEditMode( {
|
|
16
|
+
mode: 'duplicate',
|
|
17
|
+
details: {
|
|
18
|
+
postId: post.id,
|
|
19
|
+
title: post.title.rendered,
|
|
20
|
+
},
|
|
21
|
+
} );
|
|
22
|
+
};
|
|
23
|
+
|
|
12
24
|
return (
|
|
13
25
|
<ActionMenuItem
|
|
14
26
|
title={ __( 'Duplicate', 'elementor' ) }
|
|
15
27
|
icon={ CopyIcon }
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
details: {
|
|
22
|
-
postId: post.id,
|
|
23
|
-
title: post.title.rendered,
|
|
24
|
-
},
|
|
25
|
-
} );
|
|
26
|
-
} }
|
|
28
|
+
ListItemButtonProps={
|
|
29
|
+
{
|
|
30
|
+
onClick,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
27
33
|
/>
|
|
28
34
|
);
|
|
29
35
|
}
|
|
@@ -12,14 +12,18 @@ export default function Rename( { post }: { post: Post } ) {
|
|
|
12
12
|
<ActionMenuItem
|
|
13
13
|
title={ __( 'Rename', 'elementor' ) }
|
|
14
14
|
icon={ EraseIcon }
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
ListItemButtonProps={
|
|
16
|
+
{
|
|
17
|
+
onClick: () => {
|
|
18
|
+
setEditMode( {
|
|
19
|
+
mode: 'rename',
|
|
20
|
+
details: {
|
|
21
|
+
postId: post.id,
|
|
22
|
+
},
|
|
23
|
+
} );
|
|
20
24
|
},
|
|
21
|
-
}
|
|
22
|
-
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
23
27
|
/>
|
|
24
28
|
);
|
|
25
29
|
}
|
|
@@ -3,14 +3,26 @@ import { Post } from '../../../../types';
|
|
|
3
3
|
import { HomeIcon } from '@elementor/icons';
|
|
4
4
|
import { __ } from '@wordpress/i18n';
|
|
5
5
|
import ActionMenuItem from '../action-menu-item';
|
|
6
|
+
import { useHomepageActions } from '../../../../hooks/use-homepage-actions';
|
|
7
|
+
import { CircularProgress } from '@elementor/ui';
|
|
6
8
|
|
|
7
9
|
export default function SetHome( { post }: { post: Post } ) {
|
|
10
|
+
const { updateSettingsMutation } = useHomepageActions();
|
|
11
|
+
|
|
12
|
+
const handleClick = () => {
|
|
13
|
+
updateSettingsMutation.mutateAsync( { show_on_front: 'page', page_on_front: post.id } );
|
|
14
|
+
};
|
|
15
|
+
|
|
8
16
|
return (
|
|
9
17
|
<ActionMenuItem
|
|
10
18
|
title={ __( 'Set as homepage', 'elementor' ) }
|
|
11
|
-
icon={ HomeIcon }
|
|
12
|
-
|
|
13
|
-
|
|
19
|
+
icon={ ! updateSettingsMutation.isLoading ? HomeIcon : CircularProgress }
|
|
20
|
+
ListItemButtonProps={
|
|
21
|
+
{
|
|
22
|
+
disabled: !! post.isHome || post.status !== 'publish' || updateSettingsMutation.isLoading,
|
|
23
|
+
onClick: handleClick,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
14
26
|
/>
|
|
15
27
|
);
|
|
16
28
|
}
|
|
@@ -16,9 +16,11 @@ export default function View( { post }: { post: Post } ) {
|
|
|
16
16
|
<ActionMenuItem
|
|
17
17
|
title={ title }
|
|
18
18
|
icon={ EyeIcon }
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
ListItemButtonProps={
|
|
20
|
+
{
|
|
21
|
+
onClick: () => window.open( post.link, '_blank' ),
|
|
22
|
+
}
|
|
23
|
+
}
|
|
22
24
|
/>
|
|
23
25
|
);
|
|
24
26
|
}
|
|
@@ -8,13 +8,23 @@ jest.mock( '../../../../hooks/use-posts', () => ( {
|
|
|
8
8
|
usePosts: jest.fn( () => ( {
|
|
9
9
|
isLoading: false,
|
|
10
10
|
data: [
|
|
11
|
-
{ id: 1, type: 'page', title: { rendered: 'Home' }, status: '
|
|
12
|
-
{ id: 2, type: 'page', title: { rendered: 'About' }, status: '
|
|
13
|
-
{ id: 3, type: 'page', title: { rendered: 'Services' }, status: 'publish', link: 'www.test.demo', isHome: true },
|
|
11
|
+
{ id: 1, type: 'page', title: { rendered: 'Home' }, status: 'publish', link: 'www.test.demo' },
|
|
12
|
+
{ id: 2, type: 'page', title: { rendered: 'About' }, status: 'draft', link: 'www.test.demo' },
|
|
14
13
|
],
|
|
15
14
|
} ) ),
|
|
16
15
|
} ) );
|
|
17
16
|
|
|
17
|
+
jest.mock( '../../../../hooks/use-homepage', () => ( {
|
|
18
|
+
__esModule: true,
|
|
19
|
+
useHomepage: jest.fn( () => ( {
|
|
20
|
+
isLoading: false,
|
|
21
|
+
data: {
|
|
22
|
+
show_on_front: 'page',
|
|
23
|
+
page_on_front: 1,
|
|
24
|
+
},
|
|
25
|
+
} ) ),
|
|
26
|
+
} ) );
|
|
27
|
+
|
|
18
28
|
jest.mock( '@elementor/editor-documents', () => ( {
|
|
19
29
|
__esModule: true,
|
|
20
30
|
useActiveDocument: jest.fn( () => ( {
|
|
@@ -33,22 +43,27 @@ describe( '@elementor/editor-site-navigation - PostsCollapsibleList', () => {
|
|
|
33
43
|
renderWithQuery( <PostsCollapsibleList isOpenByDefault={ false } /> );
|
|
34
44
|
|
|
35
45
|
// Assert.
|
|
36
|
-
const label = screen.getByText( `Pages (
|
|
46
|
+
const label = screen.getByText( `Pages (2)` );
|
|
37
47
|
expect( label ).toBeInTheDocument();
|
|
38
48
|
|
|
39
|
-
const postInList = screen.queryByText( '
|
|
49
|
+
const postInList = screen.queryByText( 'Home' );
|
|
40
50
|
expect( postInList ).not.toBeInTheDocument();
|
|
41
51
|
} );
|
|
42
52
|
|
|
43
|
-
it( 'should render open list', () => {
|
|
53
|
+
it( 'should render open list with home icon and page status', () => {
|
|
44
54
|
// Act.
|
|
45
55
|
renderWithQuery( <PostsCollapsibleList isOpenByDefault={ true } /> );
|
|
46
56
|
|
|
47
57
|
// Assert.
|
|
48
|
-
const
|
|
49
|
-
|
|
58
|
+
const items = screen.getAllByRole( 'listitem' );
|
|
59
|
+
|
|
60
|
+
expect( items.length ).toBe( 3 );
|
|
50
61
|
|
|
51
|
-
|
|
52
|
-
expect(
|
|
62
|
+
// First item is the list title.
|
|
63
|
+
expect( items[ 0 ] ).toHaveTextContent( `Pages (2)` );
|
|
64
|
+
expect( items[ 1 ] ).toHaveTextContent( 'Home' );
|
|
65
|
+
expect( items[ 1 ] ).toHaveTextContent( 'Homepage' ); // Home icon.
|
|
66
|
+
expect( items[ 2 ] ).toHaveTextContent( 'About' );
|
|
67
|
+
expect( items[ 2 ] ).toHaveTextContent( '(draft)' );
|
|
53
68
|
} );
|
|
54
69
|
} );
|
|
@@ -20,6 +20,7 @@ import Delete from '../../actions-menu/actions/delete';
|
|
|
20
20
|
import View from '../../actions-menu/actions/view';
|
|
21
21
|
import SetHome from '../../actions-menu/actions/set-home';
|
|
22
22
|
import { Post } from '../../../../types';
|
|
23
|
+
import { __ } from '@wordpress/i18n';
|
|
23
24
|
|
|
24
25
|
export default function ListItemView( { post }: { post: Post } ) {
|
|
25
26
|
const activeDocument = useActiveDocument();
|
|
@@ -65,7 +66,7 @@ export default function ListItemView( { post }: { post: Post } ) {
|
|
|
65
66
|
</ListItemText>
|
|
66
67
|
{ post.isHome &&
|
|
67
68
|
<ListItemIcon>
|
|
68
|
-
<HomeIcon color="disabled" />
|
|
69
|
+
<HomeIcon titleAccess={ __( 'Homepage', 'elementor' ) } color="disabled" />
|
|
69
70
|
</ListItemIcon>
|
|
70
71
|
}
|
|
71
72
|
</ListItemButton>
|
|
@@ -6,6 +6,7 @@ import { usePostListContext } from '../../../contexts/post-list-context';
|
|
|
6
6
|
import { postTypesMap } from '../../../api/post';
|
|
7
7
|
import CollapsibleList from './collapsible-list';
|
|
8
8
|
import PostListItem from './post-list-item';
|
|
9
|
+
import { useHomepage } from '../../../hooks/use-homepage';
|
|
9
10
|
|
|
10
11
|
type Props = {
|
|
11
12
|
isOpenByDefault?: boolean,
|
|
@@ -14,6 +15,7 @@ type Props = {
|
|
|
14
15
|
export default function PostsCollapsibleList( { isOpenByDefault = false }: Props ) {
|
|
15
16
|
const { type, editMode } = usePostListContext();
|
|
16
17
|
const { data: posts, isLoading: postsLoading } = usePosts( type );
|
|
18
|
+
const { data: homepageSettings } = useHomepage();
|
|
17
19
|
|
|
18
20
|
if ( ! posts || postsLoading ) {
|
|
19
21
|
return (
|
|
@@ -26,6 +28,9 @@ export default function PostsCollapsibleList( { isOpenByDefault = false }: Props
|
|
|
26
28
|
|
|
27
29
|
const label = `${ postTypesMap[ type ].labels.plural_name } (${ posts.length.toString() })`;
|
|
28
30
|
|
|
31
|
+
const isHomepageSet = homepageSettings?.show_on_front === 'page' && !! homepageSettings?.page_on_front;
|
|
32
|
+
const homepageId = isHomepageSet ? homepageSettings.page_on_front : null;
|
|
33
|
+
|
|
29
34
|
return (
|
|
30
35
|
<List dense>
|
|
31
36
|
<CollapsibleList
|
|
@@ -33,7 +38,10 @@ export default function PostsCollapsibleList( { isOpenByDefault = false }: Props
|
|
|
33
38
|
Icon={ PageTypeIcon }
|
|
34
39
|
isOpenByDefault={ isOpenByDefault || false }
|
|
35
40
|
>
|
|
36
|
-
{ posts.map( ( post ) =>
|
|
41
|
+
{ posts.map( ( post ) => {
|
|
42
|
+
post = { ...post, isHome: post.id === homepageId };
|
|
43
|
+
return <PostListItem key={ post.id } post={ post } />;
|
|
44
|
+
} ) }
|
|
37
45
|
{
|
|
38
46
|
[ 'duplicate', 'create' ].includes( editMode.mode ) &&
|
|
39
47
|
<PostListItem />
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import apiFetch from '@wordpress/api-fetch';
|
|
2
|
+
import { renderHookWithQuery } from 'test-utils';
|
|
3
|
+
import { useHomepageActions } from '../use-homepage-actions';
|
|
4
|
+
import { settingsQueryKey } from '../use-homepage';
|
|
5
|
+
|
|
6
|
+
jest.mock( '@wordpress/api-fetch' );
|
|
7
|
+
|
|
8
|
+
describe( '@elementor/site-settings/use-homepage-actions', () => {
|
|
9
|
+
beforeEach( () => {
|
|
10
|
+
jest.mocked( apiFetch ).mockImplementation( () => Promise.resolve( {} ) );
|
|
11
|
+
} );
|
|
12
|
+
|
|
13
|
+
afterEach( () => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
} );
|
|
16
|
+
|
|
17
|
+
it( 'should run updateSettings from useHomepageActions hook', async () => {
|
|
18
|
+
// Arrange.
|
|
19
|
+
const { component, queryClient } = renderHookWithQuery( () => useHomepageActions() );
|
|
20
|
+
const { updateSettingsMutation } = component.result.current;
|
|
21
|
+
|
|
22
|
+
const queryKey = settingsQueryKey();
|
|
23
|
+
await queryClient.setQueryData( queryKey, {
|
|
24
|
+
show_on_front: '',
|
|
25
|
+
page_on_front: 0,
|
|
26
|
+
} );
|
|
27
|
+
|
|
28
|
+
// Act.
|
|
29
|
+
await updateSettingsMutation.mutateAsync( { show_on_front: 'page', page_on_front: 1 } );
|
|
30
|
+
|
|
31
|
+
expect( apiFetch ).toHaveBeenCalledTimes( 1 );
|
|
32
|
+
expect( apiFetch ).toHaveBeenCalledWith( {
|
|
33
|
+
path: '/wp/v2/settings',
|
|
34
|
+
method: 'POST',
|
|
35
|
+
data: {
|
|
36
|
+
show_on_front: 'page',
|
|
37
|
+
page_on_front: 1,
|
|
38
|
+
},
|
|
39
|
+
} );
|
|
40
|
+
|
|
41
|
+
expect( queryClient.getQueryState( queryKey )?.isInvalidated ).toBe( true );
|
|
42
|
+
} );
|
|
43
|
+
} );
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { waitFor } from '@testing-library/react';
|
|
2
|
+
import apiFetch from '@wordpress/api-fetch';
|
|
3
|
+
import { useHomepage } from '../use-homepage';
|
|
4
|
+
import { renderHookWithQuery } from 'test-utils';
|
|
5
|
+
|
|
6
|
+
jest.mock( '@wordpress/api-fetch' );
|
|
7
|
+
|
|
8
|
+
describe( '@elementor/site-settings/use-homepage', () => {
|
|
9
|
+
beforeEach( () => {
|
|
10
|
+
jest.mocked( apiFetch ).mockImplementation( () => Promise.resolve( [] ) );
|
|
11
|
+
} );
|
|
12
|
+
|
|
13
|
+
afterEach( () => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
} );
|
|
16
|
+
|
|
17
|
+
it( 'useHomepage hook should return homepage settings', async () => {
|
|
18
|
+
// Arrange.
|
|
19
|
+
const settings = {
|
|
20
|
+
show_on_front: 'page',
|
|
21
|
+
page_on_front: 1,
|
|
22
|
+
};
|
|
23
|
+
jest.mocked( apiFetch ).mockImplementation( () => Promise.resolve( settings ) );
|
|
24
|
+
|
|
25
|
+
// Act.
|
|
26
|
+
const { component } = renderHookWithQuery( () => useHomepage() );
|
|
27
|
+
|
|
28
|
+
// Assert.
|
|
29
|
+
const expectedPath = `/wp/v2/settings?_fields=${ encodeURIComponent( 'show_on_front,page_on_front' ) }`;
|
|
30
|
+
|
|
31
|
+
expect( apiFetch ).toHaveBeenCalledWith( {
|
|
32
|
+
path: expectedPath,
|
|
33
|
+
} );
|
|
34
|
+
expect( apiFetch ).toHaveBeenCalledTimes( 1 );
|
|
35
|
+
|
|
36
|
+
await waitFor( () => {
|
|
37
|
+
return component.result.current.isSuccess;
|
|
38
|
+
} );
|
|
39
|
+
|
|
40
|
+
expect( component.result.current.data ).toBe( settings );
|
|
41
|
+
} );
|
|
42
|
+
} );
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useMutation, useQueryClient } from '@elementor/query';
|
|
2
|
+
import { Settings, updateSettings } from '../api/settings';
|
|
3
|
+
import { settingsQueryKey } from './use-homepage';
|
|
4
|
+
|
|
5
|
+
export function useHomepageActions() {
|
|
6
|
+
const invalidateSettings = useInvalidateSettings();
|
|
7
|
+
|
|
8
|
+
const onSuccess = async () => invalidateSettings( { exact: true } );
|
|
9
|
+
|
|
10
|
+
const updateSettingsMutation = useMutation(
|
|
11
|
+
( settings: Settings ) => updateSettings( settings ),
|
|
12
|
+
{ onSuccess }
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
return { updateSettingsMutation };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function useInvalidateSettings() {
|
|
19
|
+
const queryClient = useQueryClient();
|
|
20
|
+
|
|
21
|
+
return ( options = {} ) => {
|
|
22
|
+
const queryKey = settingsQueryKey();
|
|
23
|
+
|
|
24
|
+
return queryClient.invalidateQueries( queryKey, options );
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useQuery } from '@elementor/query';
|
|
2
|
+
import { getSettings } from '../api/settings';
|
|
3
|
+
|
|
4
|
+
export const settingsQueryKey = () => [ 'site-navigation', 'homepage' ];
|
|
5
|
+
|
|
6
|
+
export function useHomepage( ) {
|
|
7
|
+
return useQuery( {
|
|
8
|
+
queryKey: settingsQueryKey(),
|
|
9
|
+
queryFn: () => getSettings(),
|
|
10
|
+
} );
|
|
11
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -8,13 +8,3 @@ export type Post = {
|
|
|
8
8
|
rendered: string;
|
|
9
9
|
}
|
|
10
10
|
};
|
|
11
|
-
|
|
12
|
-
export type PostType = {
|
|
13
|
-
name: string;
|
|
14
|
-
slug: string;
|
|
15
|
-
labels: Record<string, string>;
|
|
16
|
-
rest_base: string;
|
|
17
|
-
rest_namespace: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export type NonNullableQueryResponse<T> = T extends { data: infer U } ? T & { data: NonNullable<U> } : T;
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import { ComponentType } from 'react';
|
|
3
|
-
import { ListItemButton, ListItemIcon, ListItemText } from '@elementor/ui';
|
|
4
|
-
import { Post } from '../../../types';
|
|
5
|
-
|
|
6
|
-
export type Props = {
|
|
7
|
-
title: string;
|
|
8
|
-
icon: ComponentType;
|
|
9
|
-
disabled?: boolean;
|
|
10
|
-
onClick: ( post: Post ) => void;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export default function ActionListItem( { title, icon: Icon, disabled, onClick }: Props ) {
|
|
14
|
-
return (
|
|
15
|
-
<ListItemButton
|
|
16
|
-
disabled={ disabled }
|
|
17
|
-
onClick={ onClick }
|
|
18
|
-
>
|
|
19
|
-
<ListItemIcon>
|
|
20
|
-
<Icon />
|
|
21
|
-
</ListItemIcon>
|
|
22
|
-
<ListItemText>
|
|
23
|
-
{ title }
|
|
24
|
-
</ListItemText>
|
|
25
|
-
</ListItemButton>
|
|
26
|
-
);
|
|
27
|
-
}
|