@elementor/editor-editing-panel 0.5.1 → 0.6.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 (37) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/index.js +173 -71
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.mjs +167 -65
  5. package/dist/index.mjs.map +1 -1
  6. package/package.json +15 -7
  7. package/src/components/controls/collapsible-section.tsx +25 -0
  8. package/src/components/controls/control-types/text-area-control.tsx +5 -3
  9. package/src/components/controls/control-types/text-control.tsx +11 -0
  10. package/src/components/controls/get-control-by-type.ts +13 -0
  11. package/src/components/controls/settings-control.tsx +38 -13
  12. package/src/components/controls/style-control.tsx +35 -0
  13. package/src/components/{editing-panel-hooks.tsx → editing-panel/editing-panel-hooks.tsx} +1 -1
  14. package/src/components/editing-panel/editing-panel-tabs.tsx +26 -0
  15. package/src/components/editing-panel/editing-panel.tsx +35 -0
  16. package/src/components/editing-panel/settings-tab.tsx +64 -0
  17. package/src/components/editing-panel/style-tab.tsx +17 -0
  18. package/src/contexts/control-context.tsx +0 -25
  19. package/src/contexts/style-context.tsx +34 -0
  20. package/src/hooks/use-element-style-prop.ts +44 -0
  21. package/src/hooks/use-element-styles.ts +13 -0
  22. package/src/init.ts +1 -1
  23. package/src/panel.ts +1 -1
  24. package/src/sync/get-element-styles.ts +8 -0
  25. package/src/sync/types.ts +3 -2
  26. package/src/sync/update-style.ts +23 -0
  27. package/src/types.ts +49 -3
  28. package/src/__tests__/utils.ts +0 -28
  29. package/src/components/__tests__/editing-panel.test.tsx +0 -92
  30. package/src/components/controls/__tests__/settings-control.test.tsx +0 -71
  31. package/src/components/controls/control-types/__tests__/select-control.test.tsx +0 -67
  32. package/src/components/controls/control-types/__tests__/text-area-control.test.tsx +0 -29
  33. package/src/components/editing-panel.tsx +0 -66
  34. package/src/contexts/__tests__/control-context.test.tsx +0 -14
  35. package/src/hooks/__tests__/use-widget-settings.test.ts +0 -128
  36. package/src/sync/__tests__/get-container.test.ts +0 -40
  37. package/src/sync/__tests__/should-use-v2-panel.test.ts +0 -95
@@ -1,67 +0,0 @@
1
- import * as React from 'react';
2
- import { fireEvent, render, screen } from '@testing-library/react';
3
- import { ControlContext } from '../../../../contexts/control-context';
4
- import { SelectControl } from '../select-control';
5
-
6
- describe( 'SelectControl', () => {
7
- it( 'should pass the updated payload when select value changes', () => {
8
- // Arrange.
9
- const setValue = jest.fn();
10
- const options = [
11
- { label: 'Option 1', value: 'value1' },
12
- { label: 'Option 2', value: 'value2' },
13
- ];
14
-
15
- // Act.
16
- render(
17
- <ControlContext.Provider value={ { setValue, value: 'value1', bind: 'select' } }>
18
- <SelectControl options={ options } />
19
- </ControlContext.Provider>
20
- );
21
-
22
- const select = screen.getByRole( 'combobox' );
23
-
24
- // Assert.
25
- expect( screen.getByText( 'Option 1' ) ).toBeInTheDocument();
26
- expect( screen.queryByText( 'Option 2' ) ).not.toBeInTheDocument();
27
-
28
- // Act.
29
- fireEvent.mouseDown( select );
30
-
31
- const option2 = screen.getByText( 'Option 2' );
32
-
33
- fireEvent.click( option2 );
34
-
35
- // Assert.
36
- expect( setValue ).toHaveBeenCalledWith( 'value2' );
37
- } );
38
-
39
- it( 'should disable the select options', () => {
40
- // Arrange.
41
- const setValue = jest.fn();
42
- const options = [
43
- { label: 'Option 1', value: 'value1' },
44
- { label: 'Option 2', value: 'value2', disabled: true },
45
- { label: 'Option 3', value: 'value3', disabled: false },
46
- ];
47
-
48
- // Act.
49
- render(
50
- <ControlContext.Provider value={ { setValue, value: '', bind: 'select' } }>
51
- <SelectControl options={ options } />
52
- </ControlContext.Provider>
53
- );
54
-
55
- const select = screen.getByRole( 'combobox' );
56
-
57
- // Act.
58
- fireEvent.mouseDown( select );
59
-
60
- const option2 = screen.getByText( 'Option 2' );
61
- const option3 = screen.getByText( 'Option 3' );
62
-
63
- // Assert.
64
- expect( option2 ).toHaveAttribute( 'aria-disabled', 'true' );
65
- expect( option3 ).not.toHaveAttribute( 'aria-disabled', 'true' );
66
- } );
67
- } );
@@ -1,29 +0,0 @@
1
- import * as React from 'react';
2
- import { fireEvent, render, screen } from '@testing-library/react';
3
- import { TextAreaControl } from '../text-area-control';
4
- import { ControlContext } from '../../../../contexts/control-context';
5
-
6
- describe( 'TextAreaControl', () => {
7
- it( 'should pass the updated payload when input value changes', () => {
8
- // Arrange.
9
- const setValue = jest.fn();
10
-
11
- // Act.
12
- render(
13
- <ControlContext.Provider value={ { setValue, value: 'Hi', bind: 'text' } }>
14
- <TextAreaControl placeholder="type text here" />
15
- </ControlContext.Provider>
16
- );
17
-
18
- const input = screen.getByRole( 'textbox' );
19
-
20
- // Assert.
21
- expect( input ).toHaveValue( 'Hi' );
22
-
23
- // Act.
24
- fireEvent.input( input, { target: { value: 'Cool Heading!' } } );
25
-
26
- // Assert.
27
- expect( setValue ).toHaveBeenCalledWith( 'Cool Heading!' );
28
- } );
29
- } );
@@ -1,66 +0,0 @@
1
- import * as React from 'react';
2
- import { __ } from '@wordpress/i18n';
3
- import useSelectedElements from '../hooks/use-selected-elements';
4
- import useElementType from '../hooks/use-element-type';
5
- import { Panel, PanelBody, PanelHeader, PanelHeaderTitle } from '@elementor/editor-panels';
6
- import { SelectControl } from './controls/control-types/select-control';
7
- import { TextAreaControl } from './controls/control-types/text-area-control';
8
- import { SettingsControl } from '../components/controls/settings-control';
9
- import { Stack } from '@elementor/ui';
10
- import { ElementContext } from '../contexts/element-context';
11
-
12
- const controlTypes = {
13
- select: SelectControl,
14
- textarea: TextAreaControl,
15
- };
16
-
17
- export const EditingPanel = () => {
18
- const elements = useSelectedElements();
19
-
20
- const [ selectedElement ] = elements;
21
-
22
- const elementType = useElementType( selectedElement?.type );
23
-
24
- if ( elements.length !== 1 || ! elementType ) {
25
- return null;
26
- }
27
-
28
- /* translators: %s: Element type title. */
29
- const panelTitle = __( 'Edit %s', 'elementor' ).replace( '%s', elementType.title );
30
-
31
- return (
32
- <Panel>
33
- <PanelHeader>
34
- <PanelHeaderTitle>{ panelTitle }</PanelHeaderTitle>
35
- </PanelHeader>
36
- <PanelBody>
37
- <ElementContext element={ selectedElement }>
38
- <Stack spacing={ 2 }>
39
- { elementType.controls.map( ( control ) => {
40
- if ( control.type === 'control' ) {
41
- const ControlComponent =
42
- controlTypes[ control.value.type as keyof typeof controlTypes ];
43
-
44
- if ( ! ControlComponent ) {
45
- return null;
46
- }
47
-
48
- return (
49
- <SettingsControl key={ control.value.bind } bind={ control.value.bind }>
50
- <SettingsControl.Label>{ control.value.label }</SettingsControl.Label>
51
- <ControlComponent
52
- /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
53
- { ...( control.value.props as any ) }
54
- />
55
- </SettingsControl>
56
- );
57
- }
58
-
59
- return null;
60
- } ) }
61
- </Stack>
62
- </ElementContext>
63
- </PanelBody>
64
- </Panel>
65
- );
66
- };
@@ -1,14 +0,0 @@
1
- import { renderHook } from '@testing-library/react';
2
- import { useControl } from '../control-context';
3
-
4
- describe( 'ControlContext', () => {
5
- it( 'should throw error if used outside of ControlContext', () => {
6
- // Act & Assert.
7
- expect( () => {
8
- renderHook( () => useControl( '' ) );
9
- } ).toThrow( 'useControl must be used within a ControlContext' );
10
-
11
- // Suppress console.error from React.
12
- expect( console ).toHaveErrored();
13
- } );
14
- } );
@@ -1,128 +0,0 @@
1
- import { act, renderHook } from '@testing-library/react';
2
- import { useWidgetSettings } from '../use-widget-settings';
3
- import { ExtendedWindow } from '../../sync/types';
4
- import { mockV1Element } from '../../__tests__/utils';
5
- import { dispatchCommandAfter } from 'test-utils';
6
- import getContainer from '../../sync/get-container';
7
-
8
- jest.mock( '../../sync/get-container' );
9
-
10
- describe( 'useWidgetSettings', () => {
11
- beforeEach( () => {
12
- const extendedWindow = window as unknown as ExtendedWindow;
13
-
14
- extendedWindow.elementor = {
15
- widgetsCache: {
16
- 'v1-heading': {
17
- controls: [],
18
- title: 'Heading',
19
- },
20
- 'v2-heading': {
21
- controls: [],
22
- atomic_controls: [],
23
- title: 'Heading',
24
- },
25
- 'v2-container': {
26
- controls: [],
27
- atomic_controls: [],
28
- title: 'Container',
29
- },
30
- },
31
- };
32
- } );
33
-
34
- it( 'should return the value of the setting', () => {
35
- // Arrange.
36
- const bind = 'title';
37
- const element = { id: 'element-id' };
38
- jest.mocked( getContainer ).mockReturnValue( mockV1Element( { settings: { [ bind ]: 'Hello, World!' } } ) );
39
-
40
- // Act.
41
- const result = renderHook( () => useWidgetSettings( { id: element.id, bind } ) ).result.current;
42
-
43
- // Assert.
44
- expect( result ).toEqual( 'Hello, World!' );
45
- } );
46
-
47
- it( 'should return null if the setting is not found', () => {
48
- // Arrange.
49
- const bind = 'title';
50
- const element = { id: 'element-id' };
51
- jest.mocked( getContainer ).mockReturnValue( mockV1Element( { settings: {} } ) );
52
-
53
- // Act.
54
- const result = renderHook( () => useWidgetSettings( { id: element.id, bind } ) ).result.current;
55
-
56
- // Assert.
57
- expect( result ).toEqual( null );
58
- } );
59
-
60
- it( 'should update the state value on settings change', () => {
61
- // Arrange.
62
- const bind = 'title';
63
- const element = { id: 'element-id' };
64
- jest.mocked( getContainer ).mockReturnValue( mockV1Element( { settings: { [ bind ]: 'Hello, World!' } } ) );
65
-
66
- // Act.
67
- const { result } = renderHook( () => useWidgetSettings( { id: element.id, bind } ) );
68
-
69
- // Assert.
70
- expect( result.current ).toEqual( 'Hello, World!' );
71
-
72
- // Act.
73
- act( () => {
74
- jest.mocked( getContainer ).mockReturnValue(
75
- mockV1Element( { settings: { [ bind ]: 'Goodbye, World!' } } )
76
- );
77
- dispatchCommandAfter( 'document/elements/settings' );
78
- } );
79
-
80
- // Assert.
81
- expect( result.current ).toEqual( 'Goodbye, World!' );
82
- } );
83
-
84
- it( 'should get new snapshot for different elements', () => {
85
- // Arrange.
86
- jest.mocked( getContainer ).mockImplementation(
87
- ( id: string ) =>
88
- ( {
89
- 'element-id-1': mockV1Element( { settings: { prop: 'value' } } ),
90
- 'element-id-2': mockV1Element( { settings: { prop: 'value-2' } } ),
91
- } )[ id ] || null
92
- );
93
-
94
- // Act.
95
- const { result, rerender } = renderHook( ( props ) => useWidgetSettings( props ), {
96
- initialProps: { bind: 'prop', id: 'element-id-1' },
97
- } );
98
-
99
- // Assert.
100
- expect( result.current ).toEqual( 'value' );
101
-
102
- // Act.
103
- rerender( { bind: 'prop', id: 'element-id-2' } );
104
-
105
- // Assert.
106
- expect( result.current ).toEqual( 'value-2' );
107
- } );
108
-
109
- it( 'should get new snapshot for different binds', () => {
110
- // Arrange.
111
- jest.mocked( getContainer ).mockReturnValue(
112
- mockV1Element( { settings: { prop: 'value', 'prop-2': 'value-2' } } )
113
- );
114
-
115
- // Act.
116
- const { rerender, result } = renderHook( ( props ) => useWidgetSettings( props ), {
117
- initialProps: { bind: 'prop', id: 'element-id' },
118
- } );
119
-
120
- expect( result.current ).toEqual( 'value' );
121
-
122
- // Act.
123
- rerender( { bind: 'prop-2', id: 'element-id' } );
124
-
125
- // Assert.
126
- expect( result.current ).toEqual( 'value-2' );
127
- } );
128
- } );
@@ -1,40 +0,0 @@
1
- import getContainer from '../get-container';
2
- import { ExtendedWindow } from '../types';
3
-
4
- describe( 'getContainer', () => {
5
- const mockGetContainer = jest.fn();
6
-
7
- beforeEach( () => {
8
- const extendedWindow = window as unknown as ExtendedWindow;
9
-
10
- extendedWindow.elementor = {
11
- getContainer: mockGetContainer,
12
- };
13
- } );
14
-
15
- it( 'should return the container', () => {
16
- // Arrange.
17
- const elementID = '1-heading';
18
- const container = { id: elementID };
19
- mockGetContainer.mockReturnValue( container );
20
-
21
- // Act.
22
- const result = getContainer( elementID );
23
-
24
- // Assert.
25
- expect( result ).toBe( container );
26
- } );
27
-
28
- it( 'should return null if the container is not found', () => {
29
- // Arrange.
30
- const elementID = '1-heading';
31
- const container = undefined;
32
- mockGetContainer.mockReturnValue( container );
33
-
34
- // Act.
35
- const result = getContainer( elementID );
36
-
37
- // Assert.
38
- expect( result ).toBe( null );
39
- } );
40
- } );
@@ -1,95 +0,0 @@
1
- import { ExtendedWindow } from '../types';
2
- import { shouldUseV2Panel } from '../should-use-v2-panel';
3
- import { mockV1Element } from '../../__tests__/utils';
4
-
5
- describe( 'shouldUseV2Panel', () => {
6
- const getElements = jest.fn();
7
-
8
- beforeEach( () => {
9
- const extendedWindow = window as unknown as ExtendedWindow;
10
-
11
- extendedWindow.elementor = {
12
- selection: {
13
- getElements,
14
- },
15
- widgetsCache: {
16
- 'v1-heading': {
17
- controls: {},
18
- title: 'V1 Heading',
19
- },
20
- 'v2-heading': {
21
- controls: {},
22
- atomic_controls: [],
23
- title: 'V2 Heading',
24
- },
25
- 'v2-container': {
26
- controls: {},
27
- atomic_controls: [],
28
- title: 'V2 Container',
29
- },
30
- },
31
- };
32
- } );
33
-
34
- it( 'should return false if there are no selectedElements', () => {
35
- // Arrange.
36
- getElements.mockReturnValue( [] );
37
-
38
- // Assert.
39
- expect( shouldUseV2Panel() ).toBe( false );
40
- } );
41
-
42
- it( 'should return true for v2 element', () => {
43
- // Arrange.
44
- getElements.mockReturnValue( [ mockV1Element( { model: { widgetType: 'v2-heading' } } ) ] );
45
-
46
- // Assert.
47
- expect( shouldUseV2Panel() ).toBe( true );
48
- } );
49
-
50
- it( 'should return true for v2 element', () => {
51
- // Arrange.
52
- getElements.mockReturnValue( [ mockV1Element( { model: { elType: 'v2-container' } } ) ] );
53
-
54
- // Assert.
55
- expect( shouldUseV2Panel() ).toBe( true );
56
- } );
57
-
58
- it( 'should return false for v1 element', () => {
59
- // Arrange.
60
- getElements.mockReturnValue( [ mockV1Element( { model: { widgetType: 'v1-heading' } } ) ] );
61
-
62
- // Assert.
63
- expect( shouldUseV2Panel() ).toBe( false );
64
- } );
65
-
66
- it( 'should return false for non-existing element', () => {
67
- // Arrange.
68
- getElements.mockReturnValue( [ mockV1Element( { model: { widgetType: 'non-existing' } } ) ] );
69
-
70
- // Assert.
71
- expect( shouldUseV2Panel() ).toBe( false );
72
- } );
73
-
74
- it( 'should return false if there is more than 1 selected element with mixed versions', () => {
75
- // Arrange.
76
- getElements.mockReturnValue( [
77
- mockV1Element( { model: { widgetType: 'v2-heading' } } ),
78
- mockV1Element( { model: { widgetType: 'v1-heading' } } ),
79
- ] );
80
-
81
- // Assert.
82
- expect( shouldUseV2Panel() ).toBe( false );
83
- } );
84
-
85
- it( 'should return false if there is more than 1 selected element with same version', () => {
86
- // Arrange.
87
- getElements.mockReturnValue( [
88
- mockV1Element( { model: { widgetType: 'v2-heading' } } ),
89
- mockV1Element( { model: { widgetType: 'v2-container' } } ),
90
- ] );
91
-
92
- // Assert.
93
- expect( shouldUseV2Panel() ).toBe( false );
94
- } );
95
- } );