@sap-ux/control-property-editor 0.4.21 → 0.4.23
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 +12 -0
- package/dist/app.css +1 -1
- package/dist/app.css.map +3 -3
- package/dist/app.js +136 -136
- package/dist/app.js.map +3 -3
- package/package.json +3 -3
- package/src/App.scss +10 -1
- package/src/App.tsx +76 -73
- package/src/Workarounds.scss +5 -0
- package/src/components/ThemeSelectorCallout.tsx +3 -2
- package/src/components/index.ts +0 -1
- package/src/i18n/i18n.json +9 -1
- package/src/icons.tsx +12 -1
- package/src/index.css +1136 -470
- package/src/middleware.ts +9 -1
- package/src/panels/index.ts +1 -1
- package/src/panels/properties/index.ts +1 -0
- package/src/slice.ts +37 -2
- package/src/{panels/properties → toolbar}/DeviceSelector.tsx +1 -1
- package/src/{panels/properties → toolbar}/DeviceToggle.tsx +4 -4
- package/src/toolbar/ModeSwitcher.scss +16 -0
- package/src/toolbar/ModeSwitcher.tsx +52 -0
- package/src/{components → toolbar}/ToolBar.scss +35 -2
- package/src/toolbar/ToolBar.tsx +56 -0
- package/src/toolbar/UndoRedoSaveActions.tsx +61 -0
- package/src/{panels/properties → toolbar}/ViewChanger.tsx +2 -2
- package/src/toolbar/index.ts +1 -0
- package/test/unit/App.test.tsx +8 -3
- package/test/unit/middleware.test.ts +89 -0
- package/test/unit/panels/outline/OutlinePanel.test.tsx +5 -1
- package/test/unit/panels/properties/ViewChanger.test.tsx +1 -1
- package/test/unit/slice.test.ts +2 -1
- package/test/unit/toolbar/ModeSwitcher.test.tsx +40 -0
- package/test/unit/toolbar/UndoRedoSaveActions.test.tsx +56 -0
- package/src/components/ToolBar.tsx +0 -26
- package/src/panels/properties/PropertiesPanel.tsx +0 -30
- package/src/panels/properties/index.tsx +0 -1
- /package/src/{panels/properties → toolbar}/ViewChanger.module.scss +0 -0
- /package/test/unit/{components → toolbar}/ThemeSelector.test.tsx +0 -0
package/src/middleware.ts
CHANGED
|
@@ -8,7 +8,11 @@ import {
|
|
|
8
8
|
selectControl,
|
|
9
9
|
deletePropertyChanges,
|
|
10
10
|
addExtensionPoint,
|
|
11
|
-
reloadApplication
|
|
11
|
+
reloadApplication,
|
|
12
|
+
undo,
|
|
13
|
+
redo,
|
|
14
|
+
save,
|
|
15
|
+
setAppMode
|
|
12
16
|
} from '@sap-ux-private/control-property-editor-common';
|
|
13
17
|
|
|
14
18
|
import { changeProperty } from './slice';
|
|
@@ -47,6 +51,10 @@ export const communicationMiddleware: Middleware<Dispatch<ExternalAction>> = (st
|
|
|
47
51
|
}
|
|
48
52
|
case reloadApplication.type:
|
|
49
53
|
case deletePropertyChanges.type:
|
|
54
|
+
case setAppMode.type:
|
|
55
|
+
case undo.type:
|
|
56
|
+
case redo.type:
|
|
57
|
+
case save.type:
|
|
50
58
|
case selectControl.type:
|
|
51
59
|
case addExtensionPoint.type: {
|
|
52
60
|
sendAction(action);
|
package/src/panels/index.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { PropertiesList } from './PropertiesList';
|
package/src/slice.ts
CHANGED
|
@@ -21,7 +21,11 @@ import {
|
|
|
21
21
|
showMessage,
|
|
22
22
|
scenario,
|
|
23
23
|
reloadApplication,
|
|
24
|
-
storageFileChanged
|
|
24
|
+
storageFileChanged,
|
|
25
|
+
setAppMode,
|
|
26
|
+
setUndoRedoEnablement,
|
|
27
|
+
setSaveEnablement,
|
|
28
|
+
appLoaded
|
|
25
29
|
} from '@sap-ux-private/control-property-editor-common';
|
|
26
30
|
import { DeviceType } from './devices';
|
|
27
31
|
|
|
@@ -42,6 +46,13 @@ interface SliceState {
|
|
|
42
46
|
changes: ChangesSlice;
|
|
43
47
|
dialogMessage: ShowMessage | undefined;
|
|
44
48
|
fileChanges?: string[];
|
|
49
|
+
appMode: 'navigation' | 'adaptation';
|
|
50
|
+
changeStack: {
|
|
51
|
+
canUndo: boolean;
|
|
52
|
+
canRedo: boolean;
|
|
53
|
+
};
|
|
54
|
+
canSave: boolean;
|
|
55
|
+
isAppLoading: boolean;
|
|
45
56
|
}
|
|
46
57
|
|
|
47
58
|
export interface ChangesSlice {
|
|
@@ -120,7 +131,15 @@ export const initialState: SliceState = {
|
|
|
120
131
|
saved: [],
|
|
121
132
|
pendingChangeIds: []
|
|
122
133
|
},
|
|
123
|
-
dialogMessage: undefined
|
|
134
|
+
dialogMessage: undefined,
|
|
135
|
+
appMode: 'adaptation',
|
|
136
|
+
|
|
137
|
+
changeStack: {
|
|
138
|
+
canUndo: false,
|
|
139
|
+
canRedo: false
|
|
140
|
+
},
|
|
141
|
+
canSave: false,
|
|
142
|
+
isAppLoading: true
|
|
124
143
|
};
|
|
125
144
|
const slice = createSlice<SliceState, SliceCaseReducers<SliceState>, string>({
|
|
126
145
|
name: 'app',
|
|
@@ -265,6 +284,7 @@ const slice = createSlice<SliceState, SliceCaseReducers<SliceState>, string>({
|
|
|
265
284
|
})
|
|
266
285
|
.addMatcher(reloadApplication.match, (state): void => {
|
|
267
286
|
state.fileChanges = [];
|
|
287
|
+
state.isAppLoading = true;
|
|
268
288
|
})
|
|
269
289
|
.addMatcher(storageFileChanged.match, (state, action: ReturnType<typeof storageFileChanged>): void => {
|
|
270
290
|
const fileName = action.payload;
|
|
@@ -272,6 +292,21 @@ const slice = createSlice<SliceState, SliceCaseReducers<SliceState>, string>({
|
|
|
272
292
|
state.changes.pendingChangeIds.push(fileName);
|
|
273
293
|
}
|
|
274
294
|
})
|
|
295
|
+
.addMatcher(setAppMode.match, (state, action: ReturnType<typeof setAppMode>): void => {
|
|
296
|
+
state.appMode = action.payload;
|
|
297
|
+
})
|
|
298
|
+
.addMatcher(
|
|
299
|
+
setUndoRedoEnablement.match,
|
|
300
|
+
(state, action: ReturnType<typeof setUndoRedoEnablement>): void => {
|
|
301
|
+
state.changeStack = action.payload;
|
|
302
|
+
}
|
|
303
|
+
)
|
|
304
|
+
.addMatcher(setSaveEnablement.match, (state, action: ReturnType<typeof setSaveEnablement>): void => {
|
|
305
|
+
state.canSave = action.payload;
|
|
306
|
+
})
|
|
307
|
+
.addMatcher(appLoaded.match, (state): void => {
|
|
308
|
+
state.isAppLoading = false;
|
|
309
|
+
})
|
|
275
310
|
});
|
|
276
311
|
|
|
277
312
|
export const { setProjectScenario } = slice.actions;
|
|
@@ -2,7 +2,7 @@ import type { ReactElement } from 'react';
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
|
|
5
|
-
import { DeviceType } from '
|
|
5
|
+
import { DeviceType } from '../devices';
|
|
6
6
|
|
|
7
7
|
import type { DeviceToggleProps } from './DeviceToggle';
|
|
8
8
|
import { DeviceToggle } from './DeviceToggle';
|
|
@@ -4,9 +4,9 @@ import { useDispatch, useSelector } from 'react-redux';
|
|
|
4
4
|
import type { AnyAction } from 'redux';
|
|
5
5
|
import { UIIconButton } from '@sap-ux/ui-components';
|
|
6
6
|
|
|
7
|
-
import type { DeviceType } from '
|
|
8
|
-
import { changeDeviceType } from '
|
|
9
|
-
import type { RootState } from '
|
|
7
|
+
import type { DeviceType } from '../devices';
|
|
8
|
+
import { changeDeviceType } from '../slice';
|
|
9
|
+
import type { RootState } from '../store';
|
|
10
10
|
|
|
11
11
|
export interface DeviceToggleProps {
|
|
12
12
|
deviceType: DeviceType;
|
|
@@ -19,7 +19,7 @@ export interface DeviceToggleProps {
|
|
|
19
19
|
* @param deviceToggleProps DeviceToggleProps
|
|
20
20
|
* @returns ReactElement
|
|
21
21
|
*/
|
|
22
|
-
export function DeviceToggle(deviceToggleProps: DeviceToggleProps): ReactElement {
|
|
22
|
+
export function DeviceToggle(deviceToggleProps: Readonly<DeviceToggleProps>): ReactElement {
|
|
23
23
|
const { deviceType, tooltip } = deviceToggleProps;
|
|
24
24
|
const dispatch = useDispatch();
|
|
25
25
|
const selectedDeviceType = useSelector<RootState, DeviceType>((state) => state.deviceType);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import React, { useCallback } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
5
|
+
|
|
6
|
+
import { setAppMode } from '@sap-ux-private/control-property-editor-common';
|
|
7
|
+
import { UIDefaultButton, UILabel } from '@sap-ux/ui-components';
|
|
8
|
+
|
|
9
|
+
import type { RootState } from '../store';
|
|
10
|
+
|
|
11
|
+
import './ModeSwitcher.scss';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* React element for ModeSwitch.
|
|
15
|
+
*
|
|
16
|
+
* @returns ReactElement
|
|
17
|
+
*/
|
|
18
|
+
export function ModeSwitcher(): ReactElement {
|
|
19
|
+
const { t } = useTranslation();
|
|
20
|
+
const dispatch = useDispatch();
|
|
21
|
+
|
|
22
|
+
const mode = useSelector<RootState, 'navigation' | 'adaptation'>((state) => state.appMode);
|
|
23
|
+
const disabled = useSelector<RootState, boolean>((state) => state.isAppLoading);
|
|
24
|
+
|
|
25
|
+
const handleAdaptationClick = useCallback(() => {
|
|
26
|
+
dispatch(setAppMode('adaptation'));
|
|
27
|
+
}, [dispatch]);
|
|
28
|
+
|
|
29
|
+
const handleNavigationClick = useCallback(() => {
|
|
30
|
+
dispatch(setAppMode('navigation'));
|
|
31
|
+
}, [dispatch]);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className="mode-switcher">
|
|
35
|
+
<UILabel>{t('MODE')}:</UILabel>
|
|
36
|
+
<UIDefaultButton
|
|
37
|
+
transparent={true}
|
|
38
|
+
checked={mode === 'adaptation'}
|
|
39
|
+
onClick={handleAdaptationClick}
|
|
40
|
+
disabled={disabled}>
|
|
41
|
+
{t('EDIT')}
|
|
42
|
+
</UIDefaultButton>
|
|
43
|
+
<UIDefaultButton
|
|
44
|
+
transparent={true}
|
|
45
|
+
checked={mode === 'navigation'}
|
|
46
|
+
onClick={handleNavigationClick}
|
|
47
|
+
disabled={disabled}>
|
|
48
|
+
{t('LIVE')}
|
|
49
|
+
</UIDefaultButton>
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// Copied over from App modeler
|
|
2
1
|
.ui-toolbar {
|
|
3
2
|
// We do not need fixed if we use flexes
|
|
4
3
|
position: static;
|
|
@@ -9,11 +8,19 @@
|
|
|
9
8
|
}
|
|
10
9
|
// Move to right edge
|
|
11
10
|
.column-right {
|
|
12
|
-
|
|
11
|
+
width: 300px; // corresponds to .app-panel width
|
|
13
12
|
flex-grow: 0;
|
|
14
13
|
}
|
|
15
14
|
.column-left {
|
|
15
|
+
width: 300px; // corresponds to .app-panel width
|
|
16
16
|
overflow: hidden;
|
|
17
|
+
flex-grow: 0;
|
|
18
|
+
}
|
|
19
|
+
.column-center {
|
|
20
|
+
justify-content: flex-start;
|
|
21
|
+
.mode-switcher {
|
|
22
|
+
margin-right: 30px;
|
|
23
|
+
}
|
|
17
24
|
}
|
|
18
25
|
.ui-toolbar__content {
|
|
19
26
|
justify-content: space-between;
|
|
@@ -36,4 +43,30 @@
|
|
|
36
43
|
.ui-divider {
|
|
37
44
|
margin: 0;
|
|
38
45
|
}
|
|
46
|
+
|
|
47
|
+
.ms-Label {
|
|
48
|
+
margin-top: 0px;
|
|
49
|
+
}
|
|
39
50
|
}
|
|
51
|
+
|
|
52
|
+
.right-panel-toolbar .ui-toolbar__column__content{
|
|
53
|
+
display: flex;
|
|
54
|
+
width: 100%;
|
|
55
|
+
.rt-panel-tb-left-container {
|
|
56
|
+
display: flex;
|
|
57
|
+
flex-grow: 1;
|
|
58
|
+
align-items: center;
|
|
59
|
+
height: 100%;
|
|
60
|
+
}
|
|
61
|
+
.rt-panel-tb-right-container {
|
|
62
|
+
display: flex;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.app-title {
|
|
67
|
+
font-style: normal;
|
|
68
|
+
font-weight: bold;
|
|
69
|
+
font-size: 16px;
|
|
70
|
+
line-height: 16px;
|
|
71
|
+
color: var(--vscode-foreground);
|
|
72
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { useSelector } from 'react-redux';
|
|
5
|
+
|
|
6
|
+
import { UIFocusZone, UILabel, UIToolbar, UIToolbarColumn } from '@sap-ux/ui-components';
|
|
7
|
+
import type { Scenario } from '@sap-ux-private/control-property-editor-common';
|
|
8
|
+
|
|
9
|
+
import type { RootState } from '../store';
|
|
10
|
+
import { Separator, ThemeSelectorCallout } from '../components';
|
|
11
|
+
|
|
12
|
+
import { ViewChanger } from './ViewChanger';
|
|
13
|
+
import { DeviceSelector } from './DeviceSelector';
|
|
14
|
+
import { ModeSwitcher } from './ModeSwitcher';
|
|
15
|
+
import { UndoRedoSaveActions } from './UndoRedoSaveActions';
|
|
16
|
+
|
|
17
|
+
import './ToolBar.scss';
|
|
18
|
+
|
|
19
|
+
export interface ToolbarProps {
|
|
20
|
+
left?: React.ReactNode;
|
|
21
|
+
right?: React.ReactNode;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* React element with children.
|
|
25
|
+
*
|
|
26
|
+
* @returns ReactElement
|
|
27
|
+
*/
|
|
28
|
+
export function Toolbar(): ReactElement {
|
|
29
|
+
const { t } = useTranslation();
|
|
30
|
+
const scenario = useSelector<RootState, Scenario>((state) => state.scenario);
|
|
31
|
+
return (
|
|
32
|
+
<UIFocusZone>
|
|
33
|
+
<UIToolbar>
|
|
34
|
+
<UIToolbarColumn className="column-left">
|
|
35
|
+
<UILabel className="app-title">
|
|
36
|
+
{scenario === 'ADAPTATION_PROJECT' ? t('APP_TITLE_ADAPTATION_EDITOR') : t('APP_TITLE')}
|
|
37
|
+
</UILabel>
|
|
38
|
+
</UIToolbarColumn>
|
|
39
|
+
<UIToolbarColumn className="column-center">
|
|
40
|
+
<ModeSwitcher />
|
|
41
|
+
<UndoRedoSaveActions />
|
|
42
|
+
</UIToolbarColumn>
|
|
43
|
+
<UIToolbarColumn className="column-right right-panel-toolbar">
|
|
44
|
+
<div className="rt-panel-tb-left-container">
|
|
45
|
+
<ThemeSelectorCallout />
|
|
46
|
+
<Separator direction="vertical" style={{ marginLeft: '10px', marginRight: '10px' }} />
|
|
47
|
+
<ViewChanger />
|
|
48
|
+
</div>
|
|
49
|
+
<div className="rt-panel-tb-right-container">
|
|
50
|
+
<DeviceSelector />
|
|
51
|
+
</div>
|
|
52
|
+
</UIToolbarColumn>
|
|
53
|
+
</UIToolbar>
|
|
54
|
+
</UIFocusZone>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
3
|
+
import type { ReactElement } from 'react';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
|
|
6
|
+
import { redo, save, undo } from '@sap-ux-private/control-property-editor-common';
|
|
7
|
+
import { UIIconButton, UiIcons } from '@sap-ux/ui-components';
|
|
8
|
+
|
|
9
|
+
import type { RootState } from '../store';
|
|
10
|
+
import { Separator } from '../components';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* React element for Undo, Redo and Save.
|
|
14
|
+
*
|
|
15
|
+
* @returns ReactElement
|
|
16
|
+
*/
|
|
17
|
+
export function UndoRedoSaveActions(): ReactElement {
|
|
18
|
+
const { t } = useTranslation();
|
|
19
|
+
const dispatch = useDispatch();
|
|
20
|
+
const changeStack = useSelector<RootState, { canRedo: boolean; canUndo: boolean }>((state) => state.changeStack);
|
|
21
|
+
const canSave = useSelector<RootState, boolean>((state) => state.canSave);
|
|
22
|
+
const isLoading = useSelector<RootState, boolean>((state) => state.isAppLoading);
|
|
23
|
+
return (
|
|
24
|
+
<>
|
|
25
|
+
<UIIconButton
|
|
26
|
+
id="undo-button"
|
|
27
|
+
iconProps={{
|
|
28
|
+
iconName: UiIcons.Undo
|
|
29
|
+
}}
|
|
30
|
+
title={t('UNDO')}
|
|
31
|
+
onClick={(): void => {
|
|
32
|
+
dispatch(undo());
|
|
33
|
+
}}
|
|
34
|
+
disabled={!changeStack.canUndo || isLoading}
|
|
35
|
+
/>
|
|
36
|
+
<UIIconButton
|
|
37
|
+
id="redo-button"
|
|
38
|
+
iconProps={{
|
|
39
|
+
iconName: UiIcons.Redo
|
|
40
|
+
}}
|
|
41
|
+
title={t('REDO')}
|
|
42
|
+
onClick={(): void => {
|
|
43
|
+
dispatch(redo());
|
|
44
|
+
}}
|
|
45
|
+
disabled={!changeStack.canRedo || isLoading}
|
|
46
|
+
/>
|
|
47
|
+
<Separator direction="vertical" style={{ marginLeft: '10px', marginRight: '10px' }} />
|
|
48
|
+
<UIIconButton
|
|
49
|
+
id="save-button"
|
|
50
|
+
iconProps={{
|
|
51
|
+
iconName: UiIcons.Save
|
|
52
|
+
}}
|
|
53
|
+
title={t('SAVE')}
|
|
54
|
+
onClick={(): void => {
|
|
55
|
+
dispatch(save());
|
|
56
|
+
}}
|
|
57
|
+
disabled={!canSave || isLoading}
|
|
58
|
+
/>
|
|
59
|
+
</>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
@@ -6,8 +6,8 @@ import { useDispatch, useSelector } from 'react-redux';
|
|
|
6
6
|
import type { UIComboBoxOption, UIComboBoxRef } from '@sap-ux/ui-components';
|
|
7
7
|
import { UIComboBox, UIIconButton, UiIcons } from '@sap-ux/ui-components';
|
|
8
8
|
|
|
9
|
-
import type { RootState } from '
|
|
10
|
-
import { changePreviewScale, changePreviewScaleMode } from '
|
|
9
|
+
import type { RootState } from '../store';
|
|
10
|
+
import { changePreviewScale, changePreviewScaleMode } from '../slice';
|
|
11
11
|
|
|
12
12
|
import styles from './ViewChanger.module.scss';
|
|
13
13
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ToolBar';
|
package/test/unit/App.test.tsx
CHANGED
|
@@ -6,12 +6,12 @@ import { render, mockDomEventListener } from './utils';
|
|
|
6
6
|
import { initI18n } from '../../src/i18n';
|
|
7
7
|
|
|
8
8
|
import App from '../../src/App';
|
|
9
|
-
import { controlSelected, scenario
|
|
9
|
+
import { controlSelected, scenario } from '@sap-ux-private/control-property-editor-common';
|
|
10
10
|
import { mockResizeObserver } from '../utils/utils';
|
|
11
11
|
import { InputType } from '../../src/panels/properties/types';
|
|
12
12
|
import { registerAppIcons } from '../../src/icons';
|
|
13
13
|
import { DeviceType } from '../../src/devices';
|
|
14
|
-
import { FilterName,
|
|
14
|
+
import { FilterName, changePreviewScale, initialState } from '../../src/slice';
|
|
15
15
|
|
|
16
16
|
jest.useFakeTimers({ advanceTimers: true });
|
|
17
17
|
const windowEventListenerMock = mockDomEventListener(window);
|
|
@@ -207,7 +207,12 @@ test('renders warning message for "ADAPTATION_PROJECT" scenario', async () => {
|
|
|
207
207
|
dialogMessage: {
|
|
208
208
|
message: 'Some Text',
|
|
209
209
|
shouldHideIframe: false
|
|
210
|
-
}
|
|
210
|
+
},
|
|
211
|
+
changeStack: {
|
|
212
|
+
canUndo: true,
|
|
213
|
+
canRedo: true
|
|
214
|
+
},
|
|
215
|
+
canSave: true
|
|
211
216
|
};
|
|
212
217
|
render(<App previewUrl="" scenario="ADAPTATION_PROJECT" />, { initialState });
|
|
213
218
|
|
|
@@ -133,4 +133,93 @@ describe('communication middleware', () => {
|
|
|
133
133
|
`);
|
|
134
134
|
expect(sendActionfn).toHaveBeenCalledTimes(1);
|
|
135
135
|
});
|
|
136
|
+
|
|
137
|
+
test('undo - send action', () => {
|
|
138
|
+
const action = common.undo();
|
|
139
|
+
const next = jest.fn().mockReturnValue(action);
|
|
140
|
+
jest.mock('@sap-ux-private/control-property-editor-common', () => {
|
|
141
|
+
return {
|
|
142
|
+
undo: { type: '[ext] undo' }
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
const result = middleWare(next)(action);
|
|
146
|
+
expect(result).toMatchInlineSnapshot(`
|
|
147
|
+
Object {
|
|
148
|
+
"payload": undefined,
|
|
149
|
+
"type": "[ext] undo",
|
|
150
|
+
}
|
|
151
|
+
`);
|
|
152
|
+
expect(sendActionfn).toHaveBeenCalledTimes(1);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('redo - send action', () => {
|
|
156
|
+
const action = common.redo();
|
|
157
|
+
const next = jest.fn().mockReturnValue(action);
|
|
158
|
+
jest.mock('@sap-ux-private/control-property-editor-common', () => {
|
|
159
|
+
return {
|
|
160
|
+
redo: { type: '[ext] redo' }
|
|
161
|
+
};
|
|
162
|
+
});
|
|
163
|
+
const result = middleWare(next)(action);
|
|
164
|
+
expect(result).toMatchInlineSnapshot(`
|
|
165
|
+
Object {
|
|
166
|
+
"payload": undefined,
|
|
167
|
+
"type": "[ext] redo",
|
|
168
|
+
}
|
|
169
|
+
`);
|
|
170
|
+
expect(sendActionfn).toHaveBeenCalledTimes(1);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('save - send action', () => {
|
|
174
|
+
const action = common.save();
|
|
175
|
+
const next = jest.fn().mockReturnValue(action);
|
|
176
|
+
jest.mock('@sap-ux-private/control-property-editor-common', () => {
|
|
177
|
+
return {
|
|
178
|
+
save: { type: '[ext] save' }
|
|
179
|
+
};
|
|
180
|
+
});
|
|
181
|
+
const result = middleWare(next)(action);
|
|
182
|
+
expect(result).toMatchInlineSnapshot(`
|
|
183
|
+
Object {
|
|
184
|
+
"payload": undefined,
|
|
185
|
+
"type": "[ext] save",
|
|
186
|
+
}
|
|
187
|
+
`);
|
|
188
|
+
expect(sendActionfn).toHaveBeenCalledTimes(1);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test('setAppMode(adaptation) mode - send action', () => {
|
|
192
|
+
const action = common.setAppMode('adaptation');
|
|
193
|
+
const next = jest.fn().mockReturnValue(action);
|
|
194
|
+
jest.mock('@sap-ux-private/control-property-editor-common', () => {
|
|
195
|
+
return {
|
|
196
|
+
setAppMode: { type: '[ext] setAppMode' }
|
|
197
|
+
};
|
|
198
|
+
});
|
|
199
|
+
const result = middleWare(next)(action);
|
|
200
|
+
expect(result).toMatchInlineSnapshot(`
|
|
201
|
+
Object {
|
|
202
|
+
"payload": "adaptation",
|
|
203
|
+
"type": "[ext] set-app-mode",
|
|
204
|
+
}
|
|
205
|
+
`);
|
|
206
|
+
expect(sendActionfn).toHaveBeenCalledTimes(1);
|
|
207
|
+
});
|
|
208
|
+
test('setAppMode(navigation) mode - send action', () => {
|
|
209
|
+
const action = common.setAppMode('navigation');
|
|
210
|
+
const next = jest.fn().mockReturnValue(action);
|
|
211
|
+
jest.mock('@sap-ux-private/control-property-editor-common', () => {
|
|
212
|
+
return {
|
|
213
|
+
setAppMode: { type: '[ext] setAppMode' }
|
|
214
|
+
};
|
|
215
|
+
});
|
|
216
|
+
const result = middleWare(next)(action);
|
|
217
|
+
expect(result).toMatchInlineSnapshot(`
|
|
218
|
+
Object {
|
|
219
|
+
"payload": "navigation",
|
|
220
|
+
"type": "[ext] set-app-mode",
|
|
221
|
+
}
|
|
222
|
+
`);
|
|
223
|
+
expect(sendActionfn).toHaveBeenCalledTimes(1);
|
|
224
|
+
});
|
|
136
225
|
});
|
|
@@ -534,7 +534,11 @@ describe('OutlinePanel', () => {
|
|
|
534
534
|
},
|
|
535
535
|
icons: [],
|
|
536
536
|
dialogMessage: undefined,
|
|
537
|
-
isAdpProject: false
|
|
537
|
+
isAdpProject: false,
|
|
538
|
+
appMode: 'adaptation',
|
|
539
|
+
canSave: false,
|
|
540
|
+
changeStack: { canRedo: false, canUndo: false },
|
|
541
|
+
isAppLoading: true
|
|
538
542
|
};
|
|
539
543
|
const { container } = render(<OutlinePanel />, { initialState });
|
|
540
544
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { fireEvent, screen } from '@testing-library/react';
|
|
3
3
|
|
|
4
|
-
import { ViewChanger } from '../../../../src/
|
|
4
|
+
import { ViewChanger } from '../../../../src/toolbar/ViewChanger';
|
|
5
5
|
import { initI18n } from '../../../../src/i18n';
|
|
6
6
|
|
|
7
7
|
import { render } from '../../utils';
|
package/test/unit/slice.test.ts
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { screen } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
4
|
+
import { render } from '../utils';
|
|
5
|
+
import { initI18n } from '../../../src/i18n';
|
|
6
|
+
|
|
7
|
+
import { ModeSwitcher } from '../../../src/toolbar/ModeSwitcher';
|
|
8
|
+
import { mockResizeObserver } from '../../utils/utils';
|
|
9
|
+
import { initIcons } from '@sap-ux/ui-components';
|
|
10
|
+
import { appLoaded, setAppMode } from '@sap-ux-private/control-property-editor-common';
|
|
11
|
+
import { initialState } from '../../../src/slice';
|
|
12
|
+
|
|
13
|
+
beforeAll(() => {
|
|
14
|
+
mockResizeObserver();
|
|
15
|
+
initI18n();
|
|
16
|
+
initIcons();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('renders ModeSwitcher', () => {
|
|
20
|
+
const { dispatch, store } = render(<ModeSwitcher />, { initialState });
|
|
21
|
+
store.dispatch(appLoaded());
|
|
22
|
+
dispatch.mockClear();
|
|
23
|
+
|
|
24
|
+
expect(screen.getByText(/mode:/i)).toBeDefined();
|
|
25
|
+
const themeCalloutContent = screen.getAllByRole('button');
|
|
26
|
+
expect(themeCalloutContent).toHaveLength(2);
|
|
27
|
+
|
|
28
|
+
expect(screen.getByText(/mode:/i)).toBeInTheDocument();
|
|
29
|
+
const editBtn = screen.getByRole('button', { name: /edit/i });
|
|
30
|
+
expect(editBtn).toBeInTheDocument();
|
|
31
|
+
|
|
32
|
+
const liveBtn = screen.getByRole('button', { name: /live/i });
|
|
33
|
+
expect(liveBtn).toBeInTheDocument();
|
|
34
|
+
|
|
35
|
+
liveBtn.click();
|
|
36
|
+
expect(dispatch).toBeCalledWith(setAppMode('navigation'));
|
|
37
|
+
|
|
38
|
+
editBtn.click();
|
|
39
|
+
expect(dispatch).toBeCalledWith(setAppMode('adaptation'));
|
|
40
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { screen } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
4
|
+
import { render } from '../utils';
|
|
5
|
+
import { initI18n } from '../../../src/i18n';
|
|
6
|
+
|
|
7
|
+
import { UndoRedoSaveActions } from '../../../src/toolbar/UndoRedoSaveActions';
|
|
8
|
+
import { mockResizeObserver } from '../../utils/utils';
|
|
9
|
+
import { initIcons } from '@sap-ux/ui-components';
|
|
10
|
+
import {
|
|
11
|
+
setUndoRedoEnablement,
|
|
12
|
+
setSaveEnablement,
|
|
13
|
+
appLoaded,
|
|
14
|
+
redo,
|
|
15
|
+
save,
|
|
16
|
+
undo
|
|
17
|
+
} from '@sap-ux-private/control-property-editor-common';
|
|
18
|
+
import { initialState } from '../../../src/slice';
|
|
19
|
+
|
|
20
|
+
beforeAll(() => {
|
|
21
|
+
mockResizeObserver();
|
|
22
|
+
initI18n();
|
|
23
|
+
initIcons();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('renders UndoRedoSaveActions', () => {
|
|
27
|
+
const { dispatch, store } = render(<UndoRedoSaveActions />, { initialState });
|
|
28
|
+
|
|
29
|
+
// update state
|
|
30
|
+
store.dispatch(setUndoRedoEnablement({ canRedo: true, canUndo: true }));
|
|
31
|
+
store.dispatch(setSaveEnablement(true));
|
|
32
|
+
store.dispatch(appLoaded());
|
|
33
|
+
|
|
34
|
+
dispatch.mockClear();
|
|
35
|
+
|
|
36
|
+
const themeCalloutContent = screen.getAllByRole('button');
|
|
37
|
+
expect(themeCalloutContent).toHaveLength(3);
|
|
38
|
+
|
|
39
|
+
const undoBtn = screen.getByRole('button', { name: /undo/i });
|
|
40
|
+
expect(undoBtn).toBeInTheDocument();
|
|
41
|
+
|
|
42
|
+
const redoBtn = screen.getByRole('button', { name: /redo/i });
|
|
43
|
+
expect(redoBtn).toBeInTheDocument();
|
|
44
|
+
|
|
45
|
+
const saveBtn = screen.getByRole('button', { name: /save/i });
|
|
46
|
+
expect(saveBtn).toBeInTheDocument();
|
|
47
|
+
|
|
48
|
+
undoBtn.click();
|
|
49
|
+
expect(dispatch).toBeCalledWith(undo());
|
|
50
|
+
|
|
51
|
+
redoBtn.click();
|
|
52
|
+
expect(dispatch).toBeCalledWith(redo());
|
|
53
|
+
|
|
54
|
+
saveBtn.click();
|
|
55
|
+
expect(dispatch).toBeCalledWith(save());
|
|
56
|
+
});
|