@elementor/editor-app-bar 0.1.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.
Files changed (87) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +5 -0
  3. package/dist/index.d.ts +234 -0
  4. package/dist/index.js +1047 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/index.mjs +1017 -0
  7. package/dist/index.mjs.map +1 -0
  8. package/package.json +50 -0
  9. package/src/components/__tests__/top-bar.test.tsx +15 -0
  10. package/src/components/actions/action.tsx +33 -0
  11. package/src/components/actions/link.tsx +33 -0
  12. package/src/components/actions/toggle-action.tsx +35 -0
  13. package/src/components/app-bar.tsx +37 -0
  14. package/src/components/locations/__tests__/locations-components.test.tsx +37 -0
  15. package/src/components/locations/__tests__/menus.test.tsx +89 -0
  16. package/src/components/locations/main-menu-location.tsx +50 -0
  17. package/src/components/locations/page-indication-location.tsx +9 -0
  18. package/src/components/locations/primary-action-location.tsx +9 -0
  19. package/src/components/locations/responsive-location.tsx +9 -0
  20. package/src/components/locations/tools-menu-location.tsx +26 -0
  21. package/src/components/locations/utilities-menu-location.tsx +35 -0
  22. package/src/components/ui/popover-menu-item.tsx +39 -0
  23. package/src/components/ui/popover-menu.tsx +23 -0
  24. package/src/components/ui/toolbar-logo.tsx +80 -0
  25. package/src/components/ui/toolbar-menu-item.tsx +18 -0
  26. package/src/components/ui/toolbar-menu-more.tsx +29 -0
  27. package/src/components/ui/toolbar-menu-toggle-item.tsx +18 -0
  28. package/src/components/ui/toolbar-menu.tsx +15 -0
  29. package/src/contexts/menu-context.tsx +20 -0
  30. package/src/extensions/documents-indicator/components/__tests__/settings-button.test.tsx +46 -0
  31. package/src/extensions/documents-indicator/components/settings-button.tsx +43 -0
  32. package/src/extensions/documents-indicator/index.ts +12 -0
  33. package/src/extensions/documents-preview/hooks/__tests__/use-document-preview-props.test.ts +43 -0
  34. package/src/extensions/documents-preview/hooks/use-action-props.ts +17 -0
  35. package/src/extensions/documents-preview/index.ts +10 -0
  36. package/src/extensions/documents-save/components/__tests__/primary-action-menu.test.tsx +41 -0
  37. package/src/extensions/documents-save/components/__tests__/primary-action.test.tsx +176 -0
  38. package/src/extensions/documents-save/components/primary-action-menu.tsx +48 -0
  39. package/src/extensions/documents-save/components/primary-action.tsx +79 -0
  40. package/src/extensions/documents-save/hooks/__tests__/use-document-save-draft-props.test.ts +64 -0
  41. package/src/extensions/documents-save/hooks/__tests__/use-document-save-template-props.test.ts +27 -0
  42. package/src/extensions/documents-save/hooks/use-document-save-draft-props.ts +16 -0
  43. package/src/extensions/documents-save/hooks/use-document-save-template-props.ts +14 -0
  44. package/src/extensions/documents-save/index.ts +26 -0
  45. package/src/extensions/elements/hooks/__tests__/use-action-props.test.ts +33 -0
  46. package/src/extensions/elements/hooks/use-action-props.ts +15 -0
  47. package/src/extensions/elements/index.ts +13 -0
  48. package/src/extensions/elements/sync/__tests__/sync-panel-title.test.ts +92 -0
  49. package/src/extensions/elements/sync/sync-panel-title.ts +47 -0
  50. package/src/extensions/finder/hooks/__tests__/use-action-props.test.ts +36 -0
  51. package/src/extensions/finder/hooks/use-action-props.ts +18 -0
  52. package/src/extensions/finder/index.ts +10 -0
  53. package/src/extensions/help/index.ts +18 -0
  54. package/src/extensions/history/hooks/__tests__/use-action-props.test.ts +33 -0
  55. package/src/extensions/history/hooks/use-action-props.ts +15 -0
  56. package/src/extensions/history/index.ts +10 -0
  57. package/src/extensions/index.ts +34 -0
  58. package/src/extensions/keyboard-shortcuts/hooks/__tests__/use-action-props.test.ts +20 -0
  59. package/src/extensions/keyboard-shortcuts/hooks/use-action-props.ts +12 -0
  60. package/src/extensions/keyboard-shortcuts/index.ts +11 -0
  61. package/src/extensions/site-settings/components/__tests__/portalled-primary-action.test.tsx +94 -0
  62. package/src/extensions/site-settings/components/__tests__/primary-action.test.tsx +100 -0
  63. package/src/extensions/site-settings/components/portal.tsx +27 -0
  64. package/src/extensions/site-settings/components/portalled-primary-action.tsx +11 -0
  65. package/src/extensions/site-settings/components/primary-action.tsx +32 -0
  66. package/src/extensions/site-settings/hooks/__tests__/use-action-props.test.ts +49 -0
  67. package/src/extensions/site-settings/hooks/use-action-props.ts +22 -0
  68. package/src/extensions/site-settings/index.ts +18 -0
  69. package/src/extensions/structure/hooks/__tests__/use-action-props.test.ts +33 -0
  70. package/src/extensions/structure/hooks/use-action-props.ts +16 -0
  71. package/src/extensions/structure/index.ts +10 -0
  72. package/src/extensions/theme-builder/hooks/__tests__/use-action-props.test.ts +22 -0
  73. package/src/extensions/theme-builder/hooks/use-action-props.ts +12 -0
  74. package/src/extensions/theme-builder/index.ts +9 -0
  75. package/src/extensions/user-preferences/hooks/__tests__/use-action-props.test.ts +34 -0
  76. package/src/extensions/user-preferences/hooks/use-action-props.ts +16 -0
  77. package/src/extensions/user-preferences/index.ts +10 -0
  78. package/src/extensions/wordpress/index.ts +21 -0
  79. package/src/index.ts +13 -0
  80. package/src/init.ts +15 -0
  81. package/src/locations/__tests__/menus.test.tsx +229 -0
  82. package/src/locations/consts.ts +4 -0
  83. package/src/locations/index.ts +29 -0
  84. package/src/locations/menus.tsx +141 -0
  85. package/src/sync/__tests__/redirect-old-menus.test.ts +34 -0
  86. package/src/sync/redirect-old-menus.ts +9 -0
  87. package/src/types.ts +3 -0
@@ -0,0 +1,20 @@
1
+ import { renderHook } from '@testing-library/react-hooks';
2
+ import useActionProps from '../use-action-props';
3
+ import { runCommand } from '@elementor/editor-v1-adapters';
4
+
5
+ jest.mock( '@elementor/editor-v1-adapters', () => ( {
6
+ runCommand: jest.fn(),
7
+ } ) );
8
+
9
+ describe( '@elementor/editor-app-bar - useKeyboardShortcutsActionProps', () => {
10
+ it( 'should open the keyboard shortcuts modal on click', () => {
11
+ // Act.
12
+ const { result } = renderHook( () => useActionProps() );
13
+
14
+ result.current.onClick?.();
15
+
16
+ // Assert.
17
+ expect( runCommand ).toHaveBeenCalledTimes( 1 );
18
+ expect( runCommand ).toHaveBeenCalledWith( 'shortcuts/open' );
19
+ } );
20
+ } );
@@ -0,0 +1,12 @@
1
+ import { __ } from '@wordpress/i18n';
2
+ import { ActionProps } from '../../../types';
3
+ import { KeyboardIcon } from '@elementor/icons';
4
+ import { runCommand } from '@elementor/editor-v1-adapters';
5
+
6
+ export default function useActionProps(): ActionProps {
7
+ return {
8
+ icon: KeyboardIcon,
9
+ title: __( 'Keyboard Shortcuts', 'elementor' ),
10
+ onClick: () => runCommand( 'shortcuts/open' ),
11
+ };
12
+ }
@@ -0,0 +1,11 @@
1
+ import { mainMenu } from '../../locations';
2
+ import useActionProps from './hooks/use-action-props';
3
+
4
+ export function init() {
5
+ mainMenu.registerAction( {
6
+ name: 'open-keyboard-shortcuts',
7
+ group: 'default',
8
+ priority: 40, // After user preferences.
9
+ useProps: useActionProps,
10
+ } );
11
+ }
@@ -0,0 +1,94 @@
1
+ import * as React from 'react';
2
+ import { act, render, screen } from '@testing-library/react';
3
+ import { isRouteActive } from '@elementor/editor-v1-adapters';
4
+ import { useActiveDocument } from '@elementor/editor-documents';
5
+ import PortalledPrimaryAction from '../portalled-primary-action';
6
+ import { createMockDocument } from 'test-utils';
7
+
8
+ jest.mock( '@elementor/editor-v1-adapters', () => ( {
9
+ ...jest.requireActual( '@elementor/editor-v1-adapters' ),
10
+ isRouteActive: jest.fn(),
11
+ } ) );
12
+
13
+ jest.mock( '@elementor/editor-documents', () => ( {
14
+ useActiveDocument: jest.fn(),
15
+ useActiveDocumentActions: jest.fn( () => ( {
16
+ save: jest.fn(),
17
+ } ) ),
18
+ } ) );
19
+
20
+ describe( '@elementor/editor-app-bar - Portalled primary action', () => {
21
+ beforeEach( () => {
22
+ jest.mocked( useActiveDocument ).mockImplementation( () => createMockDocument() );
23
+ } );
24
+
25
+ afterEach( () => {
26
+ document.body.innerHTML = '';
27
+ } );
28
+
29
+ it( 'should render the button when opening the site-settings panel', () => {
30
+ // Arrange.
31
+ createRootElement();
32
+
33
+ render( <PortalledPrimaryAction /> );
34
+
35
+ // Assert.
36
+ expect( screen.queryByRole( 'button' ) ).not.toBeInTheDocument();
37
+
38
+ // Act.
39
+ jest.mocked( isRouteActive ).mockImplementation( () => true );
40
+
41
+ navigateTo( 'panel/global/menu' );
42
+
43
+ // Assert.
44
+ expect( screen.queryByRole( 'button' ) ).toBeInTheDocument();
45
+ } );
46
+
47
+ it( 'should not render the button when opening another panel', () => {
48
+ // Arrange.
49
+ createRootElement();
50
+
51
+ jest.mocked( isRouteActive ).mockImplementation( () => false );
52
+
53
+ render( <PortalledPrimaryAction /> );
54
+
55
+ // Assert.
56
+ expect( screen.queryByRole( 'button' ) ).not.toBeInTheDocument();
57
+
58
+ // Act.
59
+ jest.mocked( isRouteActive ).mockImplementation( () => true );
60
+
61
+ navigateTo( 'panel/history' );
62
+
63
+ // Assert.
64
+ expect( screen.queryByRole( 'button' ) ).not.toBeInTheDocument();
65
+ } );
66
+
67
+ it( 'should not render the button when the root element does not exist', () => {
68
+ // Arrange.
69
+ createRootElement( 'some-fake-id' );
70
+
71
+ jest.mocked( isRouteActive ).mockImplementation( () => true );
72
+
73
+ render( <PortalledPrimaryAction /> );
74
+
75
+ // Assert.
76
+ expect( screen.queryByRole( 'button' ) ).not.toBeInTheDocument();
77
+ } );
78
+ } );
79
+
80
+ function createRootElement( id = 'elementor-panel-inner' ) {
81
+ const el = document.createElement( 'div' );
82
+
83
+ el.setAttribute( 'id', id );
84
+
85
+ document.body.appendChild( el );
86
+ }
87
+
88
+ function navigateTo( route: string ) {
89
+ act( () => {
90
+ window.dispatchEvent( new CustomEvent( 'elementor/routes/open', {
91
+ detail: { route },
92
+ } ) );
93
+ } );
94
+ }
@@ -0,0 +1,100 @@
1
+ import * as React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import PrimaryAction from '../primary-action';
4
+ import { createMockDocument } from 'test-utils';
5
+ import { useActiveDocument, useActiveDocumentActions } from '@elementor/editor-documents';
6
+
7
+ jest.mock( '@elementor/editor-documents', () => ( {
8
+ useActiveDocument: jest.fn(),
9
+ useActiveDocumentActions: jest.fn(),
10
+ } ) );
11
+
12
+ describe( '@elementor/editor-app-bar - Primary action', () => {
13
+ let saveFn: () => Promise<void>;
14
+
15
+ beforeEach( () => {
16
+ saveFn = jest.fn( () => Promise.resolve() );
17
+
18
+ jest.mocked( useActiveDocument ).mockImplementation( () => createMockDocument() );
19
+ jest.mocked( useActiveDocumentActions ).mockImplementation( () => ( {
20
+ save: saveFn,
21
+ saveTemplate: jest.fn(),
22
+ saveDraft: jest.fn(),
23
+ } ) );
24
+ } );
25
+
26
+ it( 'should save site settings on click', () => {
27
+ // Arrange.
28
+ jest.mocked( useActiveDocument ).mockImplementation( () => createMockDocument( { isDirty: true } ) );
29
+
30
+ render( <PrimaryAction /> );
31
+
32
+ // Act.
33
+ screen.getByRole( 'button' ).click();
34
+
35
+ // Assert.
36
+ expect( saveFn ).toHaveBeenCalledTimes( 1 );
37
+ } );
38
+
39
+ it.each( [
40
+ {
41
+ title: "it's pristine",
42
+ document: createMockDocument( { isDirty: false } ),
43
+ },
44
+ {
45
+ title: "it's in saving mode",
46
+ document: createMockDocument( { isDirty: true, isSaving: true } ),
47
+ },
48
+ {
49
+ title: "it doesn't exist",
50
+ document: null,
51
+ },
52
+ ] )( 'should not save site settings when $title', ( { document } ) => {
53
+ // Arrange.
54
+ jest.mocked( useActiveDocument ).mockImplementation( () => document );
55
+
56
+ render( <PrimaryAction /> );
57
+
58
+ // Act.
59
+ screen.getByRole( 'button' ).click();
60
+
61
+ // Assert.
62
+ expect( saveFn ).not.toHaveBeenCalled();
63
+ } );
64
+
65
+ it.each( [
66
+ {
67
+ title: 'site settings is pristine',
68
+ document: createMockDocument( { isDirty: false } ),
69
+ },
70
+ {
71
+ title: "site settings doesn't exist",
72
+ document: null,
73
+ },
74
+ ] )( 'should be disabled when $title', ( { document } ) => {
75
+ // Arrange.
76
+ jest.mocked( useActiveDocument ).mockImplementation( () => document );
77
+
78
+ // Act.
79
+ render( <PrimaryAction /> );
80
+
81
+ // Assert.
82
+ expect( screen.getByRole( 'button' ) ).toBeDisabled();
83
+ } );
84
+
85
+ it( 'should show a loader when saving site settings', () => {
86
+ // Arrange.
87
+ jest.mocked( useActiveDocument ).mockImplementation( () => createMockDocument( {
88
+ isDirty: true,
89
+ isSaving: true,
90
+ } ) );
91
+
92
+ // Act.
93
+ const { getByRole } = render( <PrimaryAction /> );
94
+
95
+ const loader = getByRole( 'progressbar' );
96
+
97
+ // Assert.
98
+ expect( loader ).toBeInTheDocument();
99
+ } );
100
+ } );
@@ -0,0 +1,27 @@
1
+ import * as React from 'react';
2
+ import { Portal as BasePortal, PortalProps } from '@elementor/ui';
3
+ import { isRouteActive, routeCloseEvent, routeOpenEvent, useListenTo } from '@elementor/editor-v1-adapters';
4
+
5
+ export default function Portal( props: Omit<PortalProps, 'container'> ) {
6
+ const containerRef = useListenTo(
7
+ [
8
+ routeOpenEvent( 'panel/global' ),
9
+ routeCloseEvent( 'panel/global' ),
10
+ ],
11
+ getContainerRef
12
+ );
13
+
14
+ if ( ! containerRef.current ) {
15
+ return null;
16
+ }
17
+
18
+ return (
19
+ <BasePortal container={ containerRef.current } { ...props } />
20
+ );
21
+ }
22
+
23
+ function getContainerRef() {
24
+ return isRouteActive( 'panel/global' )
25
+ ? { current: document.querySelector( '#elementor-panel-inner' ) }
26
+ : { current: null };
27
+ }
@@ -0,0 +1,11 @@
1
+ import * as React from 'react';
2
+ import Portal from './portal';
3
+ import PrimaryAction from './primary-action';
4
+
5
+ export default function PortalledPrimaryAction() {
6
+ return (
7
+ <Portal>
8
+ <PrimaryAction />
9
+ </Portal>
10
+ );
11
+ }
@@ -0,0 +1,32 @@
1
+ import * as React from 'react';
2
+ import { useActiveDocument, useActiveDocumentActions } from '@elementor/editor-documents';
3
+ import { Button, CircularProgress, Paper } from '@elementor/ui';
4
+ import { __ } from '@wordpress/i18n';
5
+
6
+ export default function PrimaryAction() {
7
+ const document = useActiveDocument();
8
+ const { save } = useActiveDocumentActions();
9
+
10
+ return (
11
+ <Paper sx={ {
12
+ px: 5,
13
+ py: 4,
14
+ borderTop: 1,
15
+ borderColor: 'divider',
16
+ } }>
17
+ <Button
18
+ variant="contained"
19
+ disabled={ ! document || ! document.isDirty }
20
+ size="medium"
21
+ sx={ { width: '100%' } }
22
+ onClick={ () => document && ! document.isSaving ? save() : null }
23
+ >
24
+ {
25
+ document?.isSaving
26
+ ? <CircularProgress />
27
+ : __( 'Save Changes', 'elementor' )
28
+ }
29
+ </Button>
30
+ </Paper>
31
+ );
32
+ }
@@ -0,0 +1,49 @@
1
+ import useActionProps from '../use-action-props';
2
+ import { renderHook } from '@testing-library/react-hooks';
3
+ import { runCommand, useRouteStatus } from '@elementor/editor-v1-adapters';
4
+
5
+ jest.mock( '@elementor/editor-v1-adapters', () => ( {
6
+ runCommand: jest.fn(),
7
+ useRouteStatus: jest.fn(),
8
+ } ) );
9
+
10
+ describe( '@elementor/editor-app-bar - site-settings - useActionProps', () => {
11
+ it.each( [
12
+ {
13
+ action: 'open',
14
+ input: { isActive: false, isBlocked: false },
15
+ expectedCommand: 'panel/global/open',
16
+ },
17
+ {
18
+ action: 'close',
19
+ input: { isActive: true, isBlocked: false },
20
+ expectedCommand: 'panel/global/close',
21
+ },
22
+ ] )( 'should $action the site-settings panel on click', ( { input, expectedCommand } ) => {
23
+ // Arrange.
24
+ jest.mocked( useRouteStatus ).mockImplementation( () => input );
25
+
26
+ const { result } = renderHook( () => useActionProps() );
27
+
28
+ // Act.
29
+ result.current.onClick?.();
30
+
31
+ // Assert.
32
+ expect( runCommand ).toHaveBeenCalledTimes( 1 );
33
+ expect( runCommand ).toHaveBeenCalledWith( expectedCommand );
34
+ } );
35
+
36
+ it( 'should have the correct props for disabled and selected', () => {
37
+ // Arrange.
38
+ jest.mocked( useRouteStatus ).mockImplementation( () => ( { isActive: true, isBlocked: true } ) );
39
+
40
+ // Act.
41
+ const { result } = renderHook( () => useActionProps() );
42
+
43
+ // Assert.
44
+ expect( result.current.selected ).toBe( true );
45
+ expect( result.current.disabled ).toBe( true );
46
+ expect( useRouteStatus ).toHaveBeenCalledTimes( 1 );
47
+ expect( useRouteStatus ).toHaveBeenCalledWith( 'panel/global', { blockOnKitRoutes: false } );
48
+ } );
49
+ } );
@@ -0,0 +1,22 @@
1
+ import { __ } from '@wordpress/i18n';
2
+ import { runCommand, useRouteStatus } from '@elementor/editor-v1-adapters';
3
+ import { ToggleActionProps } from '../../../types';
4
+ import { AdjustmentsHorizontalIcon } from '@elementor/icons';
5
+
6
+ export default function useActionProps(): ToggleActionProps {
7
+ const { isActive, isBlocked } = useRouteStatus( 'panel/global', {
8
+ blockOnKitRoutes: false,
9
+ } );
10
+
11
+ return {
12
+ title: __( 'Site Settings', 'elementor' ),
13
+ icon: AdjustmentsHorizontalIcon,
14
+ onClick: () => (
15
+ isActive
16
+ ? runCommand( 'panel/global/close' )
17
+ : runCommand( 'panel/global/open' )
18
+ ),
19
+ selected: isActive,
20
+ disabled: isBlocked,
21
+ };
22
+ }
@@ -0,0 +1,18 @@
1
+ import { injectIntoTop } from '@elementor/editor';
2
+ import PortalledPrimaryAction from './components/portalled-primary-action';
3
+ import { toolsMenu } from '../../locations/index';
4
+ import useActionProps from './hooks/use-action-props';
5
+
6
+ export function init() {
7
+ // This is portal, so it injected into the top of the editor, but renders inside the site-settings panel.
8
+ injectIntoTop( {
9
+ name: 'site-settings-primary-action-portal',
10
+ filler: PortalledPrimaryAction,
11
+ } );
12
+
13
+ toolsMenu.registerToggleAction( {
14
+ name: 'toggle-site-settings',
15
+ priority: 2,
16
+ useProps: useActionProps,
17
+ } );
18
+ }
@@ -0,0 +1,33 @@
1
+ import useActionProps from '../use-action-props';
2
+ import { renderHook } from '@testing-library/react-hooks';
3
+ import { runCommand, useRouteStatus } from '@elementor/editor-v1-adapters';
4
+
5
+ jest.mock( '@elementor/editor-v1-adapters', () => ( {
6
+ runCommand: jest.fn(),
7
+ useRouteStatus: jest.fn( () => ( { isActive: true, isBlocked: true } ) ),
8
+ } ) );
9
+
10
+ describe( '@elementor/editor-app-bar - useStructureActionProps', () => {
11
+ it( 'should toggle the navigator state when clicked', () => {
12
+ // Arrange.
13
+ const { result } = renderHook( () => useActionProps() );
14
+
15
+ // Act.
16
+ result.current.onClick?.();
17
+
18
+ // Assert.
19
+ expect( runCommand ).toHaveBeenCalledTimes( 1 );
20
+ expect( runCommand ).toHaveBeenCalledWith( 'navigator/toggle' );
21
+ } );
22
+
23
+ it( 'should have the correct props for disabled and selected', () => {
24
+ // Act.
25
+ const { result } = renderHook( () => useActionProps() );
26
+
27
+ // Assert.
28
+ expect( result.current.selected ).toBe( true );
29
+ expect( result.current.disabled ).toBe( true );
30
+ expect( useRouteStatus ).toHaveBeenCalledTimes( 1 );
31
+ expect( useRouteStatus ).toHaveBeenCalledWith( 'navigator' );
32
+ } );
33
+ } );
@@ -0,0 +1,16 @@
1
+ import { __ } from '@wordpress/i18n';
2
+ import { runCommand, useRouteStatus } from '@elementor/editor-v1-adapters';
3
+ import { StructureIcon } from '@elementor/icons';
4
+ import { ToggleActionProps } from '../../../types';
5
+
6
+ export default function useActionProps(): ToggleActionProps {
7
+ const { isActive, isBlocked } = useRouteStatus( 'navigator' );
8
+
9
+ return {
10
+ title: __( 'Structure', 'elementor' ),
11
+ icon: StructureIcon,
12
+ onClick: () => runCommand( 'navigator/toggle' ),
13
+ selected: isActive,
14
+ disabled: isBlocked,
15
+ };
16
+ }
@@ -0,0 +1,10 @@
1
+ import { toolsMenu } from '../../locations';
2
+ import useActionProps from './hooks/use-action-props';
3
+
4
+ export function init() {
5
+ toolsMenu.registerToggleAction( {
6
+ name: 'toggle-structure-view',
7
+ priority: 3,
8
+ useProps: useActionProps,
9
+ } );
10
+ }
@@ -0,0 +1,22 @@
1
+ import { renderHook } from '@testing-library/react-hooks';
2
+ import useThemeBuilderActionProps from '../use-action-props';
3
+ import { runCommand } from '@elementor/editor-v1-adapters';
4
+
5
+ jest.mock( '@elementor/editor-v1-adapters', () => ( {
6
+ runCommand: jest.fn(),
7
+ } ) );
8
+
9
+ describe( '@elementor/editor-app-bar - useThemeBuilderActionProps', () => {
10
+ it( 'should open the theme builder', () => {
11
+ // Arrange.
12
+ const command = 'app/open';
13
+
14
+ // Act.
15
+ const { result } = renderHook( () => useThemeBuilderActionProps() );
16
+ result.current.onClick?.();
17
+
18
+ // Assert.
19
+ expect( runCommand ).toBeCalledTimes( 1 );
20
+ expect( runCommand ).toHaveBeenCalledWith( command );
21
+ } );
22
+ } );
@@ -0,0 +1,12 @@
1
+ import { __ } from '@wordpress/i18n';
2
+ import { ThemeBuilderIcon } from '@elementor/icons';
3
+ import { runCommand } from '@elementor/editor-v1-adapters';
4
+ import { ActionProps } from '../../../types';
5
+
6
+ export default function useActionProps(): ActionProps {
7
+ return {
8
+ icon: ThemeBuilderIcon,
9
+ title: __( 'Theme Builder', 'elementor' ),
10
+ onClick: () => runCommand( 'app/open' ),
11
+ };
12
+ }
@@ -0,0 +1,9 @@
1
+ import { mainMenu } from '../../locations';
2
+ import useThemeBuilderActionProps from './hooks/use-action-props';
3
+
4
+ export function init() {
5
+ mainMenu.registerAction( {
6
+ name: 'open-theme-builder',
7
+ useProps: useThemeBuilderActionProps,
8
+ } );
9
+ }
@@ -0,0 +1,34 @@
1
+ import useActionProps from '../use-action-props';
2
+ import { renderHook } from '@testing-library/react-hooks';
3
+ import { openRoute, useRouteStatus } from '@elementor/editor-v1-adapters';
4
+
5
+ jest.mock( '@elementor/editor-v1-adapters', () => ( {
6
+ openRoute: jest.fn(),
7
+ useRouteStatus: jest.fn( () => ( { isActive: true, isBlocked: true } ) ),
8
+ } ) );
9
+
10
+ describe( '@elementor/editor-app-bar - useUserPreferencesActionProps', () => {
11
+ it( 'should open the user preferences', () => {
12
+ // Arrange.
13
+ const route = 'panel/editor-preferences';
14
+
15
+ // Act.
16
+ const { result } = renderHook( () => useActionProps() );
17
+ result.current.onClick?.();
18
+
19
+ // Assert.
20
+ expect( openRoute ).toHaveBeenCalledTimes( 1 );
21
+ expect( openRoute ).toHaveBeenCalledWith( route );
22
+ } );
23
+
24
+ it( 'should have the correct props for disabled and selected', () => {
25
+ // Act.
26
+ const { result } = renderHook( () => useActionProps() );
27
+
28
+ // Assert.
29
+ expect( result.current.selected ).toBe( true );
30
+ expect( result.current.disabled ).toBe( true );
31
+ expect( useRouteStatus ).toHaveBeenCalledTimes( 1 );
32
+ expect( useRouteStatus ).toHaveBeenCalledWith( 'panel/editor-preferences' );
33
+ } );
34
+ } );
@@ -0,0 +1,16 @@
1
+ import { __ } from '@wordpress/i18n';
2
+ import { ToggleActionProps } from '../../../types';
3
+ import { ToggleRightIcon } from '@elementor/icons';
4
+ import { openRoute, useRouteStatus } from '@elementor/editor-v1-adapters';
5
+
6
+ export default function useActionProps(): ToggleActionProps {
7
+ const { isActive, isBlocked } = useRouteStatus( 'panel/editor-preferences' );
8
+
9
+ return {
10
+ icon: ToggleRightIcon,
11
+ title: __( 'User Preferences', 'elementor' ),
12
+ onClick: () => openRoute( 'panel/editor-preferences' ),
13
+ selected: isActive,
14
+ disabled: isBlocked,
15
+ };
16
+ }
@@ -0,0 +1,10 @@
1
+ import { mainMenu } from '../../locations';
2
+ import useActionProps from './hooks/use-action-props';
3
+
4
+ export function init() {
5
+ mainMenu.registerToggleAction( {
6
+ name: 'open-user-preferences',
7
+ priority: 30, // After history.
8
+ useProps: useActionProps,
9
+ } );
10
+ }
@@ -0,0 +1,21 @@
1
+ import { mainMenu } from '../../locations';
2
+ import { useSettings } from '@elementor/editor';
3
+ import { __ } from '@wordpress/i18n';
4
+ import { WordpressIcon } from '@elementor/icons';
5
+
6
+ export function init() {
7
+ mainMenu.registerLink( {
8
+ name: 'manage-website',
9
+ group: 'exits',
10
+ useProps: () => {
11
+ const { urls } = useSettings();
12
+
13
+ return {
14
+ title: __( 'Manage Website', 'elementor' ),
15
+ href: urls.admin,
16
+ icon: WordpressIcon,
17
+ target: '_blank',
18
+ };
19
+ },
20
+ } );
21
+ }
package/src/index.ts ADDED
@@ -0,0 +1,13 @@
1
+ export {
2
+ mainMenu,
3
+ toolsMenu,
4
+ utilitiesMenu,
5
+ documentOptionsMenu,
6
+ injectIntoPageIndication,
7
+ injectIntoResponsive,
8
+ injectIntoPrimaryAction,
9
+ } from './locations/index';
10
+
11
+ import init from './init';
12
+
13
+ init();
package/src/init.ts ADDED
@@ -0,0 +1,15 @@
1
+ import AppBar from './components/app-bar';
2
+ import { injectIntoTop } from '@elementor/editor';
3
+ import redirectOldMenus from './sync/redirect-old-menus';
4
+ import { init as initExtensions } from './extensions';
5
+
6
+ export default function init() {
7
+ redirectOldMenus();
8
+
9
+ initExtensions();
10
+
11
+ injectIntoTop( {
12
+ name: 'app-bar',
13
+ filler: AppBar,
14
+ } );
15
+ }