@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.
Files changed (39) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/app.css +1 -1
  3. package/dist/app.css.map +3 -3
  4. package/dist/app.js +136 -136
  5. package/dist/app.js.map +3 -3
  6. package/package.json +3 -3
  7. package/src/App.scss +10 -1
  8. package/src/App.tsx +76 -73
  9. package/src/Workarounds.scss +5 -0
  10. package/src/components/ThemeSelectorCallout.tsx +3 -2
  11. package/src/components/index.ts +0 -1
  12. package/src/i18n/i18n.json +9 -1
  13. package/src/icons.tsx +12 -1
  14. package/src/index.css +1136 -470
  15. package/src/middleware.ts +9 -1
  16. package/src/panels/index.ts +1 -1
  17. package/src/panels/properties/index.ts +1 -0
  18. package/src/slice.ts +37 -2
  19. package/src/{panels/properties → toolbar}/DeviceSelector.tsx +1 -1
  20. package/src/{panels/properties → toolbar}/DeviceToggle.tsx +4 -4
  21. package/src/toolbar/ModeSwitcher.scss +16 -0
  22. package/src/toolbar/ModeSwitcher.tsx +52 -0
  23. package/src/{components → toolbar}/ToolBar.scss +35 -2
  24. package/src/toolbar/ToolBar.tsx +56 -0
  25. package/src/toolbar/UndoRedoSaveActions.tsx +61 -0
  26. package/src/{panels/properties → toolbar}/ViewChanger.tsx +2 -2
  27. package/src/toolbar/index.ts +1 -0
  28. package/test/unit/App.test.tsx +8 -3
  29. package/test/unit/middleware.test.ts +89 -0
  30. package/test/unit/panels/outline/OutlinePanel.test.tsx +5 -1
  31. package/test/unit/panels/properties/ViewChanger.test.tsx +1 -1
  32. package/test/unit/slice.test.ts +2 -1
  33. package/test/unit/toolbar/ModeSwitcher.test.tsx +40 -0
  34. package/test/unit/toolbar/UndoRedoSaveActions.test.tsx +56 -0
  35. package/src/components/ToolBar.tsx +0 -26
  36. package/src/panels/properties/PropertiesPanel.tsx +0 -30
  37. package/src/panels/properties/index.tsx +0 -1
  38. /package/src/{panels/properties → toolbar}/ViewChanger.module.scss +0 -0
  39. /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);
@@ -1,2 +1,2 @@
1
- export { PropertiesPanel } from './properties';
2
1
  export { LeftPanel } from './LeftPanel';
2
+ export { PropertiesList } from './properties';
@@ -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 '../../devices';
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 '../../devices';
8
- import { changeDeviceType } from '../../slice';
9
- import type { RootState } from '../../store';
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,16 @@
1
+ .mode-switcher {
2
+ display: flex;
3
+ align-items: center;
4
+
5
+ .ms-Button {
6
+ border-radius: 3px;
7
+ margin-right: 1px;
8
+ &-label {
9
+ font-weight: bold;
10
+ }
11
+ }
12
+
13
+ .ms-Label {
14
+ margin-right: 10px;
15
+ }
16
+ }
@@ -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
- justify-content: flex-end;
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 '../../store';
10
- import { changePreviewScale, changePreviewScaleMode } from '../../slice';
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';
@@ -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, showMessage } from '@sap-ux-private/control-property-editor-common';
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, SliceState, changePreviewScale, initialState } from '../../src/slice';
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/panels/properties/ViewChanger';
4
+ import { ViewChanger } from '../../../../src/toolbar/ViewChanger';
5
5
  import { initI18n } from '../../../../src/i18n';
6
6
 
7
7
  import { render } from '../../utils';
@@ -402,7 +402,8 @@ describe('main redux slice', () => {
402
402
  reloadApplication()
403
403
  )
404
404
  ).toStrictEqual({
405
- fileChanges: []
405
+ fileChanges: [],
406
+ isAppLoading: true
406
407
  });
407
408
  });
408
409
  });
@@ -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
+ });