@elementor/editor-site-navigation 0.7.0 → 0.8.1
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 +78 -0
- package/dist/index.mjs +87 -0
- package/package.json +6 -5
- package/src/components/panel/actions-menu/action-list-item.tsx +27 -0
- package/src/components/panel/actions-menu/action-menu-item.tsx +11 -0
- package/src/components/panel/actions-menu/page-actions-menu.tsx +29 -0
- package/src/components/panel/pages-actions/delete.tsx +21 -0
- package/src/components/panel/pages-actions/duplicate.tsx +14 -0
- package/src/components/panel/pages-actions/rename.tsx +14 -0
- package/src/components/panel/pages-actions/set-home.tsx +16 -0
- package/src/components/panel/pages-actions/view.tsx +14 -0
- package/src/components/panel/pages-list/__tests__/page-list-item.test.tsx +110 -0
- package/src/components/panel/pages-list/__tests__/pages-collapsible-list.test.tsx +48 -0
- package/src/components/panel/pages-list/collapsible-list.tsx +63 -0
- package/src/components/panel/pages-list/page-list-item.tsx +66 -0
- package/src/components/panel/pages-list/pages-collapsible-list.tsx +24 -0
- package/src/components/panel/shell.tsx +58 -0
- package/src/components/shared/page-title-and-status.tsx +50 -0
- package/src/init.ts +12 -0
- package/src/types.ts +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,28 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [0.8.1](https://github.com/elementor/elementor-packages/compare/@elementor/editor-site-navigation@0.8.0...@elementor/editor-site-navigation@0.8.1) (2023-06-25)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* comment out pages panel [ED-10871] ([#64](https://github.com/elementor/elementor-packages/issues/64)) ([3a8b890](https://github.com/elementor/elementor-packages/commit/3a8b890d8f21ac816d3c0b961b37372bcb91caff))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# [0.8.0](https://github.com/elementor/elementor-packages/compare/@elementor/editor-site-navigation@0.7.0...@elementor/editor-site-navigation@0.8.0) (2023-06-25)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
* **site-navigation:** pages navigation panel UI [ED-10871] ([#51](https://github.com/elementor/elementor-packages/issues/51)) ([594427c](https://github.com/elementor/elementor-packages/commit/594427c3c296fe664577319bba29bf711f6bbdde))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
6
28
|
# [0.7.0](https://github.com/elementor/elementor-packages/compare/@elementor/editor-site-navigation@0.6.2...@elementor/editor-site-navigation@0.7.0) (2023-06-25)
|
|
7
29
|
|
|
8
30
|
|
package/dist/index.js
CHANGED
|
@@ -248,6 +248,84 @@ function RecentlyEdited() {
|
|
|
248
248
|
|
|
249
249
|
// src/init.ts
|
|
250
250
|
var import_editor_app_bar = require("@elementor/editor-app-bar");
|
|
251
|
+
var import_editor = require("@elementor/editor");
|
|
252
|
+
|
|
253
|
+
// src/components/panel/shell.tsx
|
|
254
|
+
var React18 = __toESM(require("react"));
|
|
255
|
+
var import_ui12 = require("@elementor/ui");
|
|
256
|
+
var import_icons13 = require("@elementor/icons");
|
|
257
|
+
|
|
258
|
+
// src/components/panel/pages-list/pages-collapsible-list.tsx
|
|
259
|
+
var React17 = __toESM(require("react"));
|
|
260
|
+
|
|
261
|
+
// src/components/panel/pages-list/collapsible-list.tsx
|
|
262
|
+
var React6 = __toESM(require("react"));
|
|
263
|
+
var import_react4 = require("react");
|
|
264
|
+
var import_ui6 = require("@elementor/ui");
|
|
265
|
+
var import_icons5 = require("@elementor/icons");
|
|
266
|
+
var RotateIcon = (0, import_ui6.styled)(import_icons5.ChevronDownIcon, {
|
|
267
|
+
shouldForwardProp: (prop) => prop !== "isOpen"
|
|
268
|
+
})(({ theme, isOpen }) => ({
|
|
269
|
+
transform: isOpen ? "rotate(0deg)" : "rotate(-90deg)",
|
|
270
|
+
transition: theme.transitions.create("transform", {
|
|
271
|
+
duration: theme.transitions.duration.standard
|
|
272
|
+
})
|
|
273
|
+
}));
|
|
274
|
+
|
|
275
|
+
// src/components/panel/pages-list/pages-collapsible-list.tsx
|
|
276
|
+
var import_icons12 = require("@elementor/icons");
|
|
277
|
+
|
|
278
|
+
// src/components/panel/pages-list/page-list-item.tsx
|
|
279
|
+
var React16 = __toESM(require("react"));
|
|
280
|
+
var import_ui11 = require("@elementor/ui");
|
|
281
|
+
var import_icons11 = require("@elementor/icons");
|
|
282
|
+
var import_editor_documents5 = require("@elementor/editor-documents");
|
|
283
|
+
|
|
284
|
+
// src/components/shared/page-title-and-status.tsx
|
|
285
|
+
var React7 = __toESM(require("react"));
|
|
286
|
+
var import_ui7 = require("@elementor/ui");
|
|
287
|
+
|
|
288
|
+
// src/components/panel/actions-menu/page-actions-menu.tsx
|
|
289
|
+
var React15 = __toESM(require("react"));
|
|
290
|
+
var import_ui10 = require("@elementor/ui");
|
|
291
|
+
|
|
292
|
+
// src/components/panel/pages-actions/rename.tsx
|
|
293
|
+
var React10 = __toESM(require("react"));
|
|
294
|
+
var import_icons6 = require("@elementor/icons");
|
|
295
|
+
var import_i18n3 = require("@wordpress/i18n");
|
|
296
|
+
|
|
297
|
+
// src/components/panel/actions-menu/action-menu-item.tsx
|
|
298
|
+
var React9 = __toESM(require("react"));
|
|
299
|
+
|
|
300
|
+
// src/components/panel/actions-menu/action-list-item.tsx
|
|
301
|
+
var React8 = __toESM(require("react"));
|
|
302
|
+
var import_ui8 = require("@elementor/ui");
|
|
303
|
+
|
|
304
|
+
// src/components/panel/actions-menu/action-menu-item.tsx
|
|
305
|
+
var import_ui9 = require("@elementor/ui");
|
|
306
|
+
|
|
307
|
+
// src/components/panel/pages-actions/duplicate.tsx
|
|
308
|
+
var React11 = __toESM(require("react"));
|
|
309
|
+
var import_i18n4 = require("@wordpress/i18n");
|
|
310
|
+
var import_icons7 = require("@elementor/icons");
|
|
311
|
+
|
|
312
|
+
// src/components/panel/pages-actions/delete.tsx
|
|
313
|
+
var React12 = __toESM(require("react"));
|
|
314
|
+
var import_icons8 = require("@elementor/icons");
|
|
315
|
+
var import_editor_documents4 = require("@elementor/editor-documents");
|
|
316
|
+
var import_i18n5 = require("@wordpress/i18n");
|
|
317
|
+
|
|
318
|
+
// src/components/panel/pages-actions/view.tsx
|
|
319
|
+
var React13 = __toESM(require("react"));
|
|
320
|
+
var import_icons9 = require("@elementor/icons");
|
|
321
|
+
var import_i18n6 = require("@wordpress/i18n");
|
|
322
|
+
|
|
323
|
+
// src/components/panel/pages-actions/set-home.tsx
|
|
324
|
+
var React14 = __toESM(require("react"));
|
|
325
|
+
var import_icons10 = require("@elementor/icons");
|
|
326
|
+
var import_i18n7 = require("@wordpress/i18n");
|
|
327
|
+
|
|
328
|
+
// src/init.ts
|
|
251
329
|
function init() {
|
|
252
330
|
registerTopBarMenuItems();
|
|
253
331
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -230,6 +230,93 @@ function RecentlyEdited() {
|
|
|
230
230
|
|
|
231
231
|
// src/init.ts
|
|
232
232
|
import { injectIntoPageIndication } from "@elementor/editor-app-bar";
|
|
233
|
+
import { injectIntoTop } from "@elementor/editor";
|
|
234
|
+
|
|
235
|
+
// src/components/panel/shell.tsx
|
|
236
|
+
import * as React18 from "react";
|
|
237
|
+
import { Box as Box3, Button as Button2, Divider as Divider3, Grid, List as List2, Paper, ThemeProvider, Typography as Typography4 } from "@elementor/ui";
|
|
238
|
+
import { PlusIcon as PlusIcon2 } from "@elementor/icons";
|
|
239
|
+
|
|
240
|
+
// src/components/panel/pages-list/pages-collapsible-list.tsx
|
|
241
|
+
import * as React17 from "react";
|
|
242
|
+
|
|
243
|
+
// src/components/panel/pages-list/collapsible-list.tsx
|
|
244
|
+
import * as React6 from "react";
|
|
245
|
+
import { useState as useState3 } from "react";
|
|
246
|
+
import { Collapse, IconButton, List, ListItem, ListItemIcon as ListItemIcon2, ListItemText, styled } from "@elementor/ui";
|
|
247
|
+
import { ChevronDownIcon as ChevronDownIcon2 } from "@elementor/icons";
|
|
248
|
+
var RotateIcon = styled(ChevronDownIcon2, {
|
|
249
|
+
shouldForwardProp: (prop) => prop !== "isOpen"
|
|
250
|
+
})(({ theme, isOpen }) => ({
|
|
251
|
+
transform: isOpen ? "rotate(0deg)" : "rotate(-90deg)",
|
|
252
|
+
transition: theme.transitions.create("transform", {
|
|
253
|
+
duration: theme.transitions.duration.standard
|
|
254
|
+
})
|
|
255
|
+
}));
|
|
256
|
+
|
|
257
|
+
// src/components/panel/pages-list/pages-collapsible-list.tsx
|
|
258
|
+
import { PageTypeIcon as PageTypeIcon2 } from "@elementor/icons";
|
|
259
|
+
|
|
260
|
+
// src/components/panel/pages-list/page-list-item.tsx
|
|
261
|
+
import * as React16 from "react";
|
|
262
|
+
import {
|
|
263
|
+
bindMenu as bindMenu2,
|
|
264
|
+
bindTrigger as bindTrigger2,
|
|
265
|
+
ListItem as ListItem2,
|
|
266
|
+
ListItemButton as ListItemButton2,
|
|
267
|
+
ListItemIcon as ListItemIcon4,
|
|
268
|
+
ListItemText as ListItemText3,
|
|
269
|
+
ToggleButton,
|
|
270
|
+
usePopupState as usePopupState2
|
|
271
|
+
} from "@elementor/ui";
|
|
272
|
+
import { DotsVerticalIcon, HomeIcon as HomeIcon2 } from "@elementor/icons";
|
|
273
|
+
import { useActiveDocument as useActiveDocument3, useNavigateToDocument as useNavigateToDocument3 } from "@elementor/editor-documents";
|
|
274
|
+
|
|
275
|
+
// src/components/shared/page-title-and-status.tsx
|
|
276
|
+
import * as React7 from "react";
|
|
277
|
+
import { Box as Box2, Typography as Typography3 } from "@elementor/ui";
|
|
278
|
+
|
|
279
|
+
// src/components/panel/actions-menu/page-actions-menu.tsx
|
|
280
|
+
import * as React15 from "react";
|
|
281
|
+
import { Divider as Divider2, Menu as Menu2 } from "@elementor/ui";
|
|
282
|
+
|
|
283
|
+
// src/components/panel/pages-actions/rename.tsx
|
|
284
|
+
import * as React10 from "react";
|
|
285
|
+
import { EraseIcon } from "@elementor/icons";
|
|
286
|
+
import { __ as __3 } from "@wordpress/i18n";
|
|
287
|
+
|
|
288
|
+
// src/components/panel/actions-menu/action-menu-item.tsx
|
|
289
|
+
import * as React9 from "react";
|
|
290
|
+
|
|
291
|
+
// src/components/panel/actions-menu/action-list-item.tsx
|
|
292
|
+
import * as React8 from "react";
|
|
293
|
+
import { ListItemButton, ListItemIcon as ListItemIcon3, ListItemText as ListItemText2 } from "@elementor/ui";
|
|
294
|
+
|
|
295
|
+
// src/components/panel/actions-menu/action-menu-item.tsx
|
|
296
|
+
import { MenuItem as MenuItem3 } from "@elementor/ui";
|
|
297
|
+
|
|
298
|
+
// src/components/panel/pages-actions/duplicate.tsx
|
|
299
|
+
import * as React11 from "react";
|
|
300
|
+
import { __ as __4 } from "@wordpress/i18n";
|
|
301
|
+
import { CopyIcon } from "@elementor/icons";
|
|
302
|
+
|
|
303
|
+
// src/components/panel/pages-actions/delete.tsx
|
|
304
|
+
import * as React12 from "react";
|
|
305
|
+
import { TrashIcon } from "@elementor/icons";
|
|
306
|
+
import { useActiveDocument as useActiveDocument2 } from "@elementor/editor-documents";
|
|
307
|
+
import { __ as __5 } from "@wordpress/i18n";
|
|
308
|
+
|
|
309
|
+
// src/components/panel/pages-actions/view.tsx
|
|
310
|
+
import * as React13 from "react";
|
|
311
|
+
import { EyeIcon } from "@elementor/icons";
|
|
312
|
+
import { __ as __6 } from "@wordpress/i18n";
|
|
313
|
+
|
|
314
|
+
// src/components/panel/pages-actions/set-home.tsx
|
|
315
|
+
import * as React14 from "react";
|
|
316
|
+
import { HomeIcon } from "@elementor/icons";
|
|
317
|
+
import { __ as __7 } from "@wordpress/i18n";
|
|
318
|
+
|
|
319
|
+
// src/init.ts
|
|
233
320
|
function init() {
|
|
234
321
|
registerTopBarMenuItems();
|
|
235
322
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-site-navigation",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "Elementor Team",
|
|
6
6
|
"homepage": "https://elementor.com/",
|
|
@@ -32,10 +32,11 @@
|
|
|
32
32
|
"dev": "tsup --config=../../tsup.dev.ts"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@elementor/editor
|
|
35
|
+
"@elementor/editor": "^0.6.1",
|
|
36
|
+
"@elementor/editor-app-bar": "^0.6.3",
|
|
36
37
|
"@elementor/editor-documents": "^0.8.2",
|
|
37
|
-
"@elementor/icons": "^0.
|
|
38
|
-
"@elementor/ui": "^1.4.
|
|
38
|
+
"@elementor/icons": "^0.5.0",
|
|
39
|
+
"@elementor/ui": "^1.4.53",
|
|
39
40
|
"@wordpress/api-fetch": "^6.27.0",
|
|
40
41
|
"@wordpress/i18n": "^4.31.0",
|
|
41
42
|
"@wordpress/url": "^3.31.0"
|
|
@@ -46,5 +47,5 @@
|
|
|
46
47
|
"elementor": {
|
|
47
48
|
"type": "extension"
|
|
48
49
|
},
|
|
49
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "ada7204a85dbd362c29518f8ab28defe6942a4b7"
|
|
50
51
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ComponentType } from 'react';
|
|
3
|
+
import { ListItemButton, ListItemIcon, ListItemText } from '@elementor/ui';
|
|
4
|
+
import { Page } from '../../../types';
|
|
5
|
+
|
|
6
|
+
export type Props = {
|
|
7
|
+
title: string;
|
|
8
|
+
icon: ComponentType;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
onClick: ( page: Page ) => 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
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import ActionListItem, { Props as ActionListItemProps } from './action-list-item';
|
|
3
|
+
import { MenuItem } from '@elementor/ui';
|
|
4
|
+
|
|
5
|
+
export default function ActionMenuItem( props: ActionListItemProps ) {
|
|
6
|
+
return (
|
|
7
|
+
<MenuItem disableGutters>
|
|
8
|
+
<ActionListItem { ...props } />
|
|
9
|
+
</MenuItem>
|
|
10
|
+
);
|
|
11
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Divider, Menu, MenuProps } from '@elementor/ui';
|
|
3
|
+
import Rename from '../pages-actions/rename';
|
|
4
|
+
import Duplicate from '../pages-actions/duplicate';
|
|
5
|
+
import Delete from '../pages-actions/delete';
|
|
6
|
+
import { Page } from '../../../types';
|
|
7
|
+
import View from '../pages-actions/view';
|
|
8
|
+
import SetHome from '../pages-actions/set-home';
|
|
9
|
+
|
|
10
|
+
type Props = MenuProps & {
|
|
11
|
+
page: Page
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default function PageActionsMenu( { page, ...props }: Props ) {
|
|
15
|
+
return (
|
|
16
|
+
<Menu
|
|
17
|
+
PaperProps={ { sx: { mt: 4, width: 200 } } }
|
|
18
|
+
MenuListProps={ { dense: true } }
|
|
19
|
+
{ ...props }
|
|
20
|
+
>
|
|
21
|
+
<Rename />
|
|
22
|
+
<Duplicate />
|
|
23
|
+
<Delete page={ page } />
|
|
24
|
+
<View />
|
|
25
|
+
<Divider />
|
|
26
|
+
<SetHome page={ page } />
|
|
27
|
+
</Menu>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { TrashIcon } from '@elementor/icons';
|
|
3
|
+
import { Page } from '../../../types';
|
|
4
|
+
import { useActiveDocument } from '@elementor/editor-documents';
|
|
5
|
+
import { __ } from '@wordpress/i18n';
|
|
6
|
+
import ActionMenuItem from '../actions-menu/action-menu-item';
|
|
7
|
+
|
|
8
|
+
export default function Delete( { page }: { page: Page } ) {
|
|
9
|
+
const activeDocument = useActiveDocument();
|
|
10
|
+
|
|
11
|
+
const isActive = activeDocument?.id === page.id;
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<ActionMenuItem
|
|
15
|
+
title={ __( 'Delete', 'elementor' ) }
|
|
16
|
+
icon={ TrashIcon }
|
|
17
|
+
disabled={ page.isHome || isActive }
|
|
18
|
+
onClick={ () => null }
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { __ } from '@wordpress/i18n';
|
|
3
|
+
import { CopyIcon } from '@elementor/icons';
|
|
4
|
+
import ActionMenuItem from '../actions-menu/action-menu-item';
|
|
5
|
+
|
|
6
|
+
export default function Duplicate() {
|
|
7
|
+
return (
|
|
8
|
+
<ActionMenuItem
|
|
9
|
+
title={ __( 'Duplicate', 'elementor' ) }
|
|
10
|
+
icon={ CopyIcon }
|
|
11
|
+
onClick={ () => null }
|
|
12
|
+
/>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { EraseIcon } from '@elementor/icons';
|
|
3
|
+
import { __ } from '@wordpress/i18n';
|
|
4
|
+
import ActionMenuItem from '../actions-menu/action-menu-item';
|
|
5
|
+
|
|
6
|
+
export default function Rename() {
|
|
7
|
+
return (
|
|
8
|
+
<ActionMenuItem
|
|
9
|
+
title={ __( 'Rename', 'elementor' ) }
|
|
10
|
+
icon={ EraseIcon }
|
|
11
|
+
onClick={ () => null }
|
|
12
|
+
/>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Page } from '../../../types';
|
|
3
|
+
import { HomeIcon } from '@elementor/icons';
|
|
4
|
+
import { __ } from '@wordpress/i18n';
|
|
5
|
+
import ActionMenuItem from '../actions-menu/action-menu-item';
|
|
6
|
+
|
|
7
|
+
export default function SetHome( { page }: { page: Page } ) {
|
|
8
|
+
return (
|
|
9
|
+
<ActionMenuItem
|
|
10
|
+
title={ __( 'Set as homepage', 'elementor' ) }
|
|
11
|
+
icon={ HomeIcon }
|
|
12
|
+
disabled={ !! page.isHome }
|
|
13
|
+
onClick={ () => null }
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { EyeIcon } from '@elementor/icons';
|
|
3
|
+
import { __ } from '@wordpress/i18n';
|
|
4
|
+
import ActionMenuItem from '../actions-menu/action-menu-item';
|
|
5
|
+
|
|
6
|
+
export default function View() {
|
|
7
|
+
return (
|
|
8
|
+
<ActionMenuItem
|
|
9
|
+
title={ __( 'View Page', 'elementor' ) } // TODO 21/06/2023 : post label should come from post type
|
|
10
|
+
icon={ EyeIcon }
|
|
11
|
+
onClick={ () => null }
|
|
12
|
+
/>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { act, render } from '@testing-library/react';
|
|
3
|
+
import { Page } from '../../../../types';
|
|
4
|
+
import PageListItem from '../page-list-item';
|
|
5
|
+
import { useNavigateToDocument } from '@elementor/editor-documents';
|
|
6
|
+
|
|
7
|
+
jest.mock( '@elementor/editor-documents', () => ( {
|
|
8
|
+
useActiveDocument: jest.fn(),
|
|
9
|
+
useNavigateToDocument: jest.fn(),
|
|
10
|
+
} ) );
|
|
11
|
+
|
|
12
|
+
describe( '@elementor/editor-site-navigation - PageListItem', () => {
|
|
13
|
+
afterAll( () => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
} );
|
|
16
|
+
|
|
17
|
+
it( 'should render a published post', () => {
|
|
18
|
+
// Arrange.
|
|
19
|
+
const page: Page = {
|
|
20
|
+
id: 1,
|
|
21
|
+
title: 'Test Post',
|
|
22
|
+
status: 'publish',
|
|
23
|
+
type: 'page',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Act.
|
|
27
|
+
const { getByText, queryByText } = render( <PageListItem page={ page } /> );
|
|
28
|
+
|
|
29
|
+
// Assert.
|
|
30
|
+
const label = getByText( 'Test Post' );
|
|
31
|
+
const publishedLabel = queryByText( 'publish', { exact: false } );
|
|
32
|
+
|
|
33
|
+
expect( label ).toBeInTheDocument();
|
|
34
|
+
expect( publishedLabel ).toBeNull();
|
|
35
|
+
} );
|
|
36
|
+
|
|
37
|
+
it( 'should show the post status for non-published posts', () => {
|
|
38
|
+
// Arrange.
|
|
39
|
+
const page: Page = {
|
|
40
|
+
id: 1,
|
|
41
|
+
title: 'Test Post',
|
|
42
|
+
status: 'draft',
|
|
43
|
+
type: 'page',
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Act.
|
|
47
|
+
const { getByText } = render( <PageListItem page={ page } /> );
|
|
48
|
+
|
|
49
|
+
// Assert.
|
|
50
|
+
const label = getByText( 'draft', { exact: false } );
|
|
51
|
+
expect( label ).toBeInTheDocument();
|
|
52
|
+
} );
|
|
53
|
+
|
|
54
|
+
it( 'should render actions menu', () => {
|
|
55
|
+
// Arrange.
|
|
56
|
+
const page: Page = {
|
|
57
|
+
id: 1,
|
|
58
|
+
title: 'Test Post',
|
|
59
|
+
status: 'draft',
|
|
60
|
+
type: 'page',
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const actions = [ 'View Page', 'Rename', 'Duplicate', 'Delete', 'Set as homepage' ];
|
|
64
|
+
|
|
65
|
+
// Act.
|
|
66
|
+
const { getByText, getAllByRole } = render( <PageListItem page={ page } /> );
|
|
67
|
+
const buttons = getAllByRole( 'button' );
|
|
68
|
+
// Button to open menu
|
|
69
|
+
const button = buttons[ 1 ];
|
|
70
|
+
|
|
71
|
+
act( () => {
|
|
72
|
+
// Open menu
|
|
73
|
+
button.click();
|
|
74
|
+
} );
|
|
75
|
+
|
|
76
|
+
// Assert.
|
|
77
|
+
actions.forEach( ( action ) => {
|
|
78
|
+
const label = getByText( action );
|
|
79
|
+
expect( label ).toBeInTheDocument();
|
|
80
|
+
} );
|
|
81
|
+
} );
|
|
82
|
+
|
|
83
|
+
it( 'should navigate to document on click', () => {
|
|
84
|
+
// Arrange.
|
|
85
|
+
const navigateToDocument = jest.fn();
|
|
86
|
+
jest.mocked( useNavigateToDocument ).mockReturnValue( navigateToDocument );
|
|
87
|
+
|
|
88
|
+
const id = 10;
|
|
89
|
+
|
|
90
|
+
const page: Page = {
|
|
91
|
+
id,
|
|
92
|
+
title: 'Test Post',
|
|
93
|
+
status: 'draft',
|
|
94
|
+
type: 'page',
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Act.
|
|
98
|
+
const { getAllByRole } = render( <PageListItem page={ page } /> );
|
|
99
|
+
const buttons = getAllByRole( 'button' );
|
|
100
|
+
const button = buttons[ 0 ];
|
|
101
|
+
|
|
102
|
+
act( () => {
|
|
103
|
+
button.click();
|
|
104
|
+
} );
|
|
105
|
+
|
|
106
|
+
// Assert.
|
|
107
|
+
expect( navigateToDocument ).toHaveBeenCalledTimes( 1 );
|
|
108
|
+
expect( navigateToDocument ).toHaveBeenCalledWith( id );
|
|
109
|
+
} );
|
|
110
|
+
} );
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import { Page } from '../../../../types';
|
|
4
|
+
import PagesCollapsibleList from '../pages-collapsible-list';
|
|
5
|
+
|
|
6
|
+
jest.mock( '@elementor/editor-documents' );
|
|
7
|
+
|
|
8
|
+
describe( '@elementor/editor-site-navigation - PagesCollapsibleList', () => {
|
|
9
|
+
const mockPosts: Page[] = [
|
|
10
|
+
{ id: 1, type: 'page', title: 'Home', status: 'draft' },
|
|
11
|
+
{ id: 2, type: 'page', title: 'About', status: 'publish' },
|
|
12
|
+
{ id: 3, type: 'page', title: 'Services', status: 'publish', isHome: true },
|
|
13
|
+
{ id: 4, type: 'page', title: 'Contact', status: 'draft' },
|
|
14
|
+
{ id: 5, type: 'page', title: 'FAQ', status: 'publish' },
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
afterAll( () => {
|
|
18
|
+
jest.clearAllMocks();
|
|
19
|
+
} );
|
|
20
|
+
|
|
21
|
+
it( 'should render closed list', () => {
|
|
22
|
+
// Act.
|
|
23
|
+
const { getByText, queryByText } = render(
|
|
24
|
+
<PagesCollapsibleList pages={ mockPosts } isOpenByDefault={ false } />,
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// Assert.
|
|
28
|
+
const label = getByText( `Pages (${ mockPosts.length })` );
|
|
29
|
+
expect( label ).toBeInTheDocument();
|
|
30
|
+
|
|
31
|
+
const postInList = queryByText( 'Services' );
|
|
32
|
+
expect( postInList ).toBeNull();
|
|
33
|
+
} );
|
|
34
|
+
|
|
35
|
+
it( 'should render open list', () => {
|
|
36
|
+
// Act.
|
|
37
|
+
const { getByText } = render(
|
|
38
|
+
<PagesCollapsibleList pages={ mockPosts } isOpenByDefault={ true } />,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
// Assert.
|
|
42
|
+
const label = getByText( `Pages (${ mockPosts.length })` );
|
|
43
|
+
expect( label ).toBeInTheDocument();
|
|
44
|
+
|
|
45
|
+
const postInList = getByText( 'Services' );
|
|
46
|
+
expect( postInList ).toBeInTheDocument();
|
|
47
|
+
} );
|
|
48
|
+
} );
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ComponentType, PropsWithChildren, useState } from 'react';
|
|
3
|
+
import { Collapse, IconButton, List, ListItem, ListItemIcon, ListItemText, styled, SvgIconProps } from '@elementor/ui';
|
|
4
|
+
import { ChevronDownIcon } from '@elementor/icons';
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
label: string
|
|
8
|
+
Icon: ComponentType;
|
|
9
|
+
isOpenByDefault?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface RotateIconProps extends SvgIconProps {
|
|
13
|
+
isOpen: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// TODO 21/06/2023 : Should replace this with future Rotate component that will be implemented in elementor-ui
|
|
17
|
+
const RotateIcon = styled( ChevronDownIcon, {
|
|
18
|
+
shouldForwardProp: ( prop ) => prop !== 'isOpen',
|
|
19
|
+
} )<RotateIconProps>( ( { theme, isOpen } ) => ( {
|
|
20
|
+
transform: isOpen ? 'rotate(0deg)' : 'rotate(-90deg)',
|
|
21
|
+
transition: theme.transitions.create( 'transform', {
|
|
22
|
+
duration: theme.transitions.duration.standard,
|
|
23
|
+
} ),
|
|
24
|
+
} ) );
|
|
25
|
+
|
|
26
|
+
export default function CollapsibleList(
|
|
27
|
+
{
|
|
28
|
+
label,
|
|
29
|
+
Icon,
|
|
30
|
+
isOpenByDefault = false,
|
|
31
|
+
children,
|
|
32
|
+
}: PropsWithChildren<Props>
|
|
33
|
+
) {
|
|
34
|
+
const [ isOpen, setIsOpen ] = useState( isOpenByDefault );
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<>
|
|
38
|
+
<ListItem disableGutters>
|
|
39
|
+
<ListItemIcon>
|
|
40
|
+
<IconButton
|
|
41
|
+
onClick={ () => setIsOpen( ( prev ) => ! prev ) }
|
|
42
|
+
sx={ { color: 'inherit' } }
|
|
43
|
+
size="small"
|
|
44
|
+
>
|
|
45
|
+
<RotateIcon fontSize="small" isOpen={ isOpen } />
|
|
46
|
+
</IconButton>
|
|
47
|
+
</ListItemIcon>
|
|
48
|
+
<ListItemIcon>
|
|
49
|
+
<Icon />
|
|
50
|
+
</ListItemIcon>
|
|
51
|
+
<ListItemText>{ label }</ListItemText>
|
|
52
|
+
</ListItem>
|
|
53
|
+
<Collapse
|
|
54
|
+
in={ isOpen }
|
|
55
|
+
timeout="auto"
|
|
56
|
+
unmountOnExit>
|
|
57
|
+
<List dense>
|
|
58
|
+
{ children }
|
|
59
|
+
</List>
|
|
60
|
+
</Collapse>
|
|
61
|
+
</>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
bindMenu,
|
|
4
|
+
bindTrigger,
|
|
5
|
+
ListItem,
|
|
6
|
+
ListItemButton,
|
|
7
|
+
ListItemIcon,
|
|
8
|
+
ListItemText,
|
|
9
|
+
ToggleButton,
|
|
10
|
+
usePopupState,
|
|
11
|
+
} from '@elementor/ui';
|
|
12
|
+
import { DotsVerticalIcon, HomeIcon } from '@elementor/icons';
|
|
13
|
+
import { Page } from '../../../types';
|
|
14
|
+
import { useActiveDocument, useNavigateToDocument } from '@elementor/editor-documents';
|
|
15
|
+
import PageTitleAndStatus from '../../shared/page-title-and-status';
|
|
16
|
+
import PageActionsMenu from '../actions-menu/page-actions-menu';
|
|
17
|
+
|
|
18
|
+
export default function PageListItem( { page }: { page: Page } ) {
|
|
19
|
+
const popupState = usePopupState( {
|
|
20
|
+
variant: 'popover',
|
|
21
|
+
popupId: 'page-actions',
|
|
22
|
+
} );
|
|
23
|
+
|
|
24
|
+
const activeDocument = useActiveDocument();
|
|
25
|
+
const navigateToDocument = useNavigateToDocument();
|
|
26
|
+
|
|
27
|
+
const isActive = activeDocument?.id === page.id;
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<>
|
|
31
|
+
<ListItem
|
|
32
|
+
disablePadding
|
|
33
|
+
secondaryAction={
|
|
34
|
+
<ToggleButton
|
|
35
|
+
value
|
|
36
|
+
color="secondary"
|
|
37
|
+
size="small"
|
|
38
|
+
selected={ popupState.isOpen }
|
|
39
|
+
{ ...bindTrigger( popupState ) }
|
|
40
|
+
>
|
|
41
|
+
<DotsVerticalIcon fontSize="small" />
|
|
42
|
+
</ToggleButton>
|
|
43
|
+
}
|
|
44
|
+
>
|
|
45
|
+
<ListItemButton
|
|
46
|
+
selected={ isActive }
|
|
47
|
+
onClick={ () => navigateToDocument( page.id ) }
|
|
48
|
+
dense
|
|
49
|
+
>
|
|
50
|
+
<ListItemIcon />
|
|
51
|
+
<ListItemText
|
|
52
|
+
disableTypography={ true }
|
|
53
|
+
>
|
|
54
|
+
<PageTitleAndStatus page={ page } />
|
|
55
|
+
</ListItemText>
|
|
56
|
+
{ page.isHome &&
|
|
57
|
+
<ListItemIcon>
|
|
58
|
+
<HomeIcon color="disabled" />
|
|
59
|
+
</ListItemIcon>
|
|
60
|
+
}
|
|
61
|
+
</ListItemButton>
|
|
62
|
+
</ListItem>
|
|
63
|
+
<PageActionsMenu page={ page } { ...bindMenu( popupState ) } />
|
|
64
|
+
</>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import CollapsibleList from './collapsible-list';
|
|
3
|
+
import { Page } from '../../../types';
|
|
4
|
+
import { PageTypeIcon } from '@elementor/icons';
|
|
5
|
+
import PageListItem from './page-list-item';
|
|
6
|
+
|
|
7
|
+
type Props = {
|
|
8
|
+
pages: Page[];
|
|
9
|
+
isOpenByDefault?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function PagesCollapsibleList( { pages, isOpenByDefault = false }: Props ) {
|
|
13
|
+
const label = `Pages (${ pages.length })`; // TODO 21/06/2023 : This label should come from the backend
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<CollapsibleList
|
|
17
|
+
label={ label }
|
|
18
|
+
Icon={ PageTypeIcon }
|
|
19
|
+
isOpenByDefault={ isOpenByDefault }
|
|
20
|
+
>
|
|
21
|
+
{ pages.map( ( page ) => <PageListItem key={ page.id } page={ page } /> ) }
|
|
22
|
+
</CollapsibleList>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Box, Button, Divider, Grid, List, Paper, ThemeProvider, Typography } from '@elementor/ui';
|
|
3
|
+
import { PlusIcon } from '@elementor/icons';
|
|
4
|
+
import { Page } from '../../types';
|
|
5
|
+
import PagesCollapsibleList from './pages-list/pages-collapsible-list';
|
|
6
|
+
|
|
7
|
+
// TODO: Remove once connected to real data fetching.
|
|
8
|
+
const mockPages: Page[] = [
|
|
9
|
+
{
|
|
10
|
+
id: 1,
|
|
11
|
+
type: 'page',
|
|
12
|
+
title: 'This is a very long title that somebody wrote, a very very long line',
|
|
13
|
+
status: 'pending approval',
|
|
14
|
+
},
|
|
15
|
+
{ id: 2, type: 'page', title: 'About', status: 'publish' },
|
|
16
|
+
{ id: 3, type: 'page', title: 'Services', status: 'publish', isHome: true },
|
|
17
|
+
{ id: 4, type: 'page', title: 'Contact', status: 'draft' },
|
|
18
|
+
{ id: 5, type: 'page', title: 'FAQ', status: 'publish' },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const Shell = () => {
|
|
22
|
+
return (
|
|
23
|
+
<ThemeProvider colorScheme="light">
|
|
24
|
+
<Box sx={ { width: '100%', maxWidth: 360 } }>
|
|
25
|
+
<Paper>
|
|
26
|
+
<Grid
|
|
27
|
+
container
|
|
28
|
+
justifyContent="center"
|
|
29
|
+
alignItems="center"
|
|
30
|
+
sx={ { height: 51 } }>
|
|
31
|
+
<Typography variant="h6">Pages</Typography>
|
|
32
|
+
</Grid>
|
|
33
|
+
<Divider />
|
|
34
|
+
<Box
|
|
35
|
+
display="flex"
|
|
36
|
+
justifyContent="flex-end"
|
|
37
|
+
alignItems="center"
|
|
38
|
+
>
|
|
39
|
+
<Button
|
|
40
|
+
sx={ { mt: 4, mb: 4, mr: 5 } }
|
|
41
|
+
startIcon={ <PlusIcon /> }
|
|
42
|
+
>
|
|
43
|
+
Add New
|
|
44
|
+
</Button>
|
|
45
|
+
</Box>
|
|
46
|
+
<Box sx={ { width: '100%', maxWidth: 360 } }>
|
|
47
|
+
<List dense>
|
|
48
|
+
<PagesCollapsibleList pages={ mockPages } isOpenByDefault={ true } />
|
|
49
|
+
</List>
|
|
50
|
+
</Box>
|
|
51
|
+
<Divider />
|
|
52
|
+
</Paper>
|
|
53
|
+
</Box>
|
|
54
|
+
</ThemeProvider>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default Shell;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Box, Typography } from '@elementor/ui';
|
|
3
|
+
import { Page } from '../../types';
|
|
4
|
+
import { useReverseHtmlEntities } from '../../hooks/use-reverse-html-entities';
|
|
5
|
+
|
|
6
|
+
const PageStatus = ( { status }: { status: string } ) => {
|
|
7
|
+
if ( 'publish' === status ) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<Typography
|
|
13
|
+
component="span"
|
|
14
|
+
variant="body1"
|
|
15
|
+
sx={ {
|
|
16
|
+
textTransform: 'capitalize',
|
|
17
|
+
fontStyle: 'italic',
|
|
18
|
+
whiteSpace: 'nowrap',
|
|
19
|
+
flexBasis: 'content',
|
|
20
|
+
} }
|
|
21
|
+
>
|
|
22
|
+
({ status })
|
|
23
|
+
</Typography>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const PageTitle = ( { title }: { title: string } ) => {
|
|
28
|
+
const modifiedTitle = useReverseHtmlEntities( title );
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Typography
|
|
32
|
+
component="span"
|
|
33
|
+
variant="body1"
|
|
34
|
+
noWrap
|
|
35
|
+
sx={ {
|
|
36
|
+
flexBasis: 'auto',
|
|
37
|
+
} }
|
|
38
|
+
>
|
|
39
|
+
{ modifiedTitle }
|
|
40
|
+
</Typography>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export default function PageTitleAndStatus( { page }: { page: Page } ) {
|
|
45
|
+
return (
|
|
46
|
+
<Box display="flex">
|
|
47
|
+
<PageTitle title={ page.title } /> <PageStatus status={ page.status } />
|
|
48
|
+
</Box>
|
|
49
|
+
);
|
|
50
|
+
}
|
package/src/init.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import RecentlyEdited from './components/top-bar/recently-edited';
|
|
2
2
|
import { injectIntoPageIndication } from '@elementor/editor-app-bar';
|
|
3
|
+
import { injectIntoTop } from '@elementor/editor';
|
|
4
|
+
import Shell from './components/panel/shell';
|
|
3
5
|
|
|
4
6
|
export default function init() {
|
|
5
7
|
registerTopBarMenuItems();
|
|
8
|
+
// TODO 06/06/2023 : uncomment registerPanel() when we are ready to release
|
|
9
|
+
// registerPanel();
|
|
6
10
|
}
|
|
7
11
|
|
|
8
12
|
function registerTopBarMenuItems() {
|
|
@@ -11,3 +15,11 @@ function registerTopBarMenuItems() {
|
|
|
11
15
|
filler: RecentlyEdited,
|
|
12
16
|
} );
|
|
13
17
|
}
|
|
18
|
+
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
20
|
+
function registerPanel() {
|
|
21
|
+
injectIntoTop( {
|
|
22
|
+
id: 'navigation-panel',
|
|
23
|
+
filler: Shell,
|
|
24
|
+
} );
|
|
25
|
+
}
|