@sap-ux/control-property-editor 0.2.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 (112) hide show
  1. package/.eslintignore +1 -0
  2. package/.eslintrc.js +16 -0
  3. package/CHANGELOG.md +7 -0
  4. package/LICENSE +201 -0
  5. package/README.md +16 -0
  6. package/dist/app.css +2 -0
  7. package/dist/app.css.map +7 -0
  8. package/dist/app.js +347 -0
  9. package/dist/app.js.map +7 -0
  10. package/esbuild.js +25 -0
  11. package/jest.config.js +20 -0
  12. package/package.json +68 -0
  13. package/src/App.scss +57 -0
  14. package/src/App.tsx +136 -0
  15. package/src/Workarounds.scss +79 -0
  16. package/src/actions.ts +3 -0
  17. package/src/components/AppLogo.module.scss +8 -0
  18. package/src/components/AppLogo.tsx +75 -0
  19. package/src/components/ChangeIndicator.tsx +80 -0
  20. package/src/components/Separator.tsx +32 -0
  21. package/src/components/ThemeSelectorCallout.scss +48 -0
  22. package/src/components/ThemeSelectorCallout.tsx +125 -0
  23. package/src/components/ToolBar.scss +39 -0
  24. package/src/components/ToolBar.tsx +26 -0
  25. package/src/components/index.ts +4 -0
  26. package/src/devices.ts +18 -0
  27. package/src/global.d.ts +4 -0
  28. package/src/i18n/i18n.json +68 -0
  29. package/src/i18n.ts +25 -0
  30. package/src/icons.tsx +198 -0
  31. package/src/index.css +1288 -0
  32. package/src/index.tsx +47 -0
  33. package/src/middleware.ts +54 -0
  34. package/src/panels/LeftPanel.scss +17 -0
  35. package/src/panels/LeftPanel.tsx +48 -0
  36. package/src/panels/changes/ChangeStack.module.scss +3 -0
  37. package/src/panels/changes/ChangeStack.tsx +219 -0
  38. package/src/panels/changes/ChangeStackHeader.tsx +43 -0
  39. package/src/panels/changes/ChangesPanel.module.scss +18 -0
  40. package/src/panels/changes/ChangesPanel.tsx +90 -0
  41. package/src/panels/changes/ControlGroup.module.scss +17 -0
  42. package/src/panels/changes/ControlGroup.tsx +61 -0
  43. package/src/panels/changes/PropertyChange.module.scss +24 -0
  44. package/src/panels/changes/PropertyChange.tsx +159 -0
  45. package/src/panels/changes/UnknownChange.module.scss +46 -0
  46. package/src/panels/changes/UnknownChange.tsx +96 -0
  47. package/src/panels/changes/index.tsx +3 -0
  48. package/src/panels/changes/utils.ts +36 -0
  49. package/src/panels/index.ts +2 -0
  50. package/src/panels/outline/Funnel.tsx +64 -0
  51. package/src/panels/outline/NoControlFound.tsx +45 -0
  52. package/src/panels/outline/OutlinePanel.scss +98 -0
  53. package/src/panels/outline/OutlinePanel.tsx +38 -0
  54. package/src/panels/outline/Tree.tsx +393 -0
  55. package/src/panels/outline/index.ts +1 -0
  56. package/src/panels/outline/utils.ts +154 -0
  57. package/src/panels/properties/Clipboard.tsx +44 -0
  58. package/src/panels/properties/DeviceSelector.tsx +40 -0
  59. package/src/panels/properties/DeviceToggle.tsx +39 -0
  60. package/src/panels/properties/DropdownEditor.tsx +80 -0
  61. package/src/panels/properties/Funnel.tsx +64 -0
  62. package/src/panels/properties/HeaderField.tsx +150 -0
  63. package/src/panels/properties/IconValueHelp.tsx +203 -0
  64. package/src/panels/properties/InputTypeSelector.tsx +20 -0
  65. package/src/panels/properties/InputTypeToggle.module.scss +4 -0
  66. package/src/panels/properties/InputTypeToggle.tsx +79 -0
  67. package/src/panels/properties/InputTypeWrapper.tsx +259 -0
  68. package/src/panels/properties/NoControlSelected.tsx +38 -0
  69. package/src/panels/properties/Properties.scss +102 -0
  70. package/src/panels/properties/PropertiesList.tsx +162 -0
  71. package/src/panels/properties/PropertiesPanel.tsx +30 -0
  72. package/src/panels/properties/PropertyDocumentation.module.scss +81 -0
  73. package/src/panels/properties/PropertyDocumentation.tsx +174 -0
  74. package/src/panels/properties/SapUiIcon.scss +109 -0
  75. package/src/panels/properties/StringEditor.tsx +122 -0
  76. package/src/panels/properties/ViewChanger.module.scss +5 -0
  77. package/src/panels/properties/ViewChanger.tsx +143 -0
  78. package/src/panels/properties/constants.ts +2 -0
  79. package/src/panels/properties/index.tsx +1 -0
  80. package/src/panels/properties/propertyValuesCache.ts +39 -0
  81. package/src/panels/properties/types.ts +49 -0
  82. package/src/slice.ts +216 -0
  83. package/src/store.ts +19 -0
  84. package/src/use-local-storage.ts +40 -0
  85. package/src/use-window-size.ts +39 -0
  86. package/src/variables.scss +2 -0
  87. package/test/unit/App.test.tsx +207 -0
  88. package/test/unit/appIndex.test.ts +23 -0
  89. package/test/unit/components/ChangeIndicator.test.tsx +120 -0
  90. package/test/unit/components/ThemeSelector.test.tsx +41 -0
  91. package/test/unit/middleware.test.ts +116 -0
  92. package/test/unit/panels/changes/ChangesPanel.test.tsx +261 -0
  93. package/test/unit/panels/changes/utils.test.ts +40 -0
  94. package/test/unit/panels/outline/OutlinePanel.test.tsx +353 -0
  95. package/test/unit/panels/outline/__snapshots__/utils.test.ts.snap +36 -0
  96. package/test/unit/panels/outline/utils.test.ts +83 -0
  97. package/test/unit/panels/properties/Clipboard.test.tsx +18 -0
  98. package/test/unit/panels/properties/DropdownEditor.test.tsx +62 -0
  99. package/test/unit/panels/properties/Funnel.test.tsx +34 -0
  100. package/test/unit/panels/properties/HeaderField.test.tsx +36 -0
  101. package/test/unit/panels/properties/IconValueHelp.test.tsx +60 -0
  102. package/test/unit/panels/properties/InputTypeToggle.test.tsx +126 -0
  103. package/test/unit/panels/properties/InputTypeWrapper.test.tsx +430 -0
  104. package/test/unit/panels/properties/PropertyDocumentation.test.tsx +131 -0
  105. package/test/unit/panels/properties/StringEditor.test.tsx +107 -0
  106. package/test/unit/panels/properties/ViewChanger.test.tsx +190 -0
  107. package/test/unit/panels/properties/propertyValuesCache.test.ts +23 -0
  108. package/test/unit/slice.test.ts +268 -0
  109. package/test/unit/utils.tsx +67 -0
  110. package/test/utils/utils.tsx +25 -0
  111. package/tsconfig.eslint.json +4 -0
  112. package/tsconfig.json +39 -0
@@ -0,0 +1,353 @@
1
+ import React from 'react';
2
+ import { screen, fireEvent } from '@testing-library/react';
3
+ import { initIcons } from '@sap-ux/ui-components';
4
+
5
+ import { render } from '../../utils';
6
+ import { initI18n } from '../../../../src/i18n';
7
+ import { mockResizeObserver } from '../../../utils/utils';
8
+ import { OutlinePanel } from '../../../../src/panels/outline';
9
+ import type { OutlineNode } from '@sap-ux-private/control-property-editor-common';
10
+ import { controlSelected, outlineChanged } from '@sap-ux-private/control-property-editor-common';
11
+ import type { FilterOptions, default as reducer } from '../../../../src/slice';
12
+ import { FilterName, filterNodes } from '../../../../src/slice';
13
+ import { DeviceType } from '../../../../src/devices';
14
+ import { registerAppIcons } from '../../../../src/icons';
15
+
16
+ export type State = ReturnType<typeof reducer>;
17
+
18
+ const getModel = (editable = true, visible = true, toggleParent = false, toggleChildren = false): OutlineNode[] => {
19
+ const model: OutlineNode[] = [
20
+ {
21
+ name: 'one',
22
+ controlId: '01',
23
+ children: [
24
+ {
25
+ name: 'first child of one',
26
+ controlId: '01-01',
27
+ children: [],
28
+ controlType: 'name.space.first.child.one',
29
+ editable: toggleChildren ? !toggleChildren : editable,
30
+ visible: toggleChildren ? !toggleChildren : visible
31
+ },
32
+ {
33
+ name: 'second child of one',
34
+ controlId: '01-02',
35
+ children: [],
36
+ controlType: 'name.space.second.child.one',
37
+ editable: toggleChildren ? !toggleChildren : editable,
38
+ visible: toggleChildren ? !toggleChildren : visible
39
+ },
40
+ {
41
+ name: 'third child of one',
42
+ controlId: '01-03',
43
+ children: [],
44
+ controlType: 'name.space.third.child.one',
45
+ editable: toggleChildren ? !toggleChildren : editable,
46
+ visible: toggleChildren ? !toggleChildren : visible
47
+ }
48
+ ],
49
+ controlType: 'name.space.one',
50
+ editable: toggleParent ? !toggleParent : editable,
51
+ visible: toggleParent ? !toggleParent : visible
52
+ },
53
+ {
54
+ name: 'two',
55
+ controlId: '02',
56
+ children: [],
57
+ controlType: 'name.space.two',
58
+ editable: toggleParent ? !toggleParent : editable,
59
+ visible: toggleParent ? !toggleParent : visible
60
+ },
61
+ {
62
+ name: 'SmartTable',
63
+ controlId: '03',
64
+ children: [],
65
+ controlType: 'sap.ui.comp.smarttable.SmartTable',
66
+ editable: toggleParent ? !toggleParent : editable,
67
+ visible: toggleParent ? !toggleParent : visible
68
+ }
69
+ ];
70
+ return model;
71
+ };
72
+ const filterInitOptions: FilterOptions[] = [
73
+ { name: FilterName.focusEditable, value: false },
74
+ { name: FilterName.focusCommonlyUsed, value: false },
75
+ { name: FilterName.query, value: '' }
76
+ ];
77
+ describe('OutlinePanel', () => {
78
+ beforeAll(() => {
79
+ mockResizeObserver();
80
+ initI18n();
81
+ initIcons();
82
+ registerAppIcons();
83
+ });
84
+ test('initial load', () => {
85
+ const { container } = render(<OutlinePanel />);
86
+ // check search box
87
+ const search = screen.getByRole('searchbox');
88
+ expect(search).toBeInTheDocument();
89
+ // check funnel
90
+ const funnelIcon = container.querySelector('[data-icon-name="funnel"]') as Element;
91
+ expect(funnelIcon).toBeInTheDocument();
92
+ // check no tree is rendered
93
+ const noCtrFoundText = screen.getByText('No control found');
94
+ expect(noCtrFoundText).toHaveTextContent('No control found');
95
+ const modifySearchInputText = screen.getByText('Modify the search input');
96
+ expect(modifySearchInputText).toHaveTextContent('Modify the search input');
97
+ const noSearchMatchedIcon = screen.getByTestId('Control-Property-Editor-No-Search-Matched-Icon');
98
+ expect(noSearchMatchedIcon).toBeInTheDocument();
99
+ });
100
+ test('tree', () => {
101
+ const model = getModel(true, false);
102
+ const initialState: State = {
103
+ deviceType: DeviceType.Desktop,
104
+ scale: 1,
105
+ outline: model,
106
+ filterQuery: filterInitOptions,
107
+ selectedControl: undefined,
108
+ changes: {
109
+ pending: [],
110
+ saved: [],
111
+ controls: {}
112
+ },
113
+ icons: []
114
+ };
115
+ const { container } = render(<OutlinePanel />, { initialState });
116
+ // check one
117
+ const one = screen.getAllByText(/one/i)[0];
118
+ expect(one).toBeInTheDocument();
119
+ // check two
120
+ const two = screen.getByText(/two/i);
121
+ expect(two).toBeInTheDocument();
122
+ // click on arrow right of one
123
+ // Note: react testing lib does not expand it
124
+ const arrowRight = container.querySelector('[data-icon-name="Chevron"]') as Element;
125
+ expect(arrowRight).toBeInTheDocument();
126
+ fireEvent.click(arrowRight);
127
+ // check first child of one
128
+ const firstChildOfOne = screen.getByText(/first child of one/i);
129
+ expect(firstChildOfOne).toBeInTheDocument();
130
+ // check second child of one
131
+ const secondChildOfOne = screen.getByText(/second child of one/i);
132
+ expect(secondChildOfOne).toBeInTheDocument();
133
+ });
134
+ test('query tree', () => {
135
+ const model = getModel(true, false);
136
+ const initialState: State = {
137
+ deviceType: DeviceType.Desktop,
138
+ scale: 1,
139
+ outline: model,
140
+ filterQuery: filterInitOptions,
141
+ selectedControl: undefined,
142
+ changes: {
143
+ pending: [],
144
+ saved: [],
145
+ controls: {}
146
+ },
147
+ icons: []
148
+ };
149
+ render(<OutlinePanel />, { initialState });
150
+ const search = screen.getByRole('searchbox');
151
+ // trigger search with query
152
+ fireEvent.change(search, { target: { value: 'second' } });
153
+ // check query value
154
+ expect((search as any).value).toEqual('second');
155
+ // first child of one is filtered
156
+ const firstChildOfOne = screen.queryByText(/first child of one/i);
157
+ expect(firstChildOfOne).toBeNull();
158
+ // second child of one matches query
159
+ const secondChildOfOne = screen.getByText(/second child of one/i);
160
+ expect(secondChildOfOne).toBeInTheDocument();
161
+ });
162
+
163
+ test('focus editable controls of tree', () => {
164
+ const model: OutlineNode[] = [
165
+ {
166
+ name: 'one',
167
+ controlId: '01',
168
+ children: [],
169
+ controlType: 'name.space.one',
170
+ editable: false,
171
+ visible: false
172
+ },
173
+ {
174
+ name: 'two',
175
+ controlId: '02',
176
+ children: [],
177
+ controlType: 'name.space.two',
178
+ editable: true,
179
+ visible: false
180
+ }
181
+ ];
182
+ const initialState: State = {
183
+ deviceType: DeviceType.Desktop,
184
+ scale: 1,
185
+ outline: model,
186
+ filterQuery: filterInitOptions,
187
+ selectedControl: undefined,
188
+ changes: {
189
+ pending: [],
190
+ saved: [],
191
+ controls: {}
192
+ },
193
+ icons: []
194
+ };
195
+ const { container } = render(<OutlinePanel />, { initialState });
196
+ let focusEditableRow = container.querySelector('.focusEditable');
197
+ // class 'focusEditable' is not set
198
+ expect(focusEditableRow).toBeNull();
199
+ const funnelIcon = container.querySelector('[data-icon-name="funnel"]') as Element;
200
+ // open callout
201
+ fireEvent.click(funnelIcon);
202
+ const focusEditable = screen.getByText(/focus editable/i);
203
+ // click on focus editable checkbox
204
+ fireEvent.click(focusEditable);
205
+ // class 'focusEditable' is set
206
+ focusEditableRow = container.querySelector('.focusEditable');
207
+ expect(focusEditableRow).toBeInTheDocument();
208
+ });
209
+
210
+ test('show only commonly used controls of tree', () => {
211
+ const model: OutlineNode[] = [
212
+ {
213
+ name: 'one',
214
+ controlId: '01',
215
+ children: [],
216
+ controlType: 'name.space.one',
217
+ editable: true,
218
+ visible: true
219
+ },
220
+ {
221
+ name: 'two',
222
+ controlId: '02',
223
+ children: [],
224
+ controlType: 'name.space.two',
225
+ editable: false,
226
+ visible: false
227
+ },
228
+ {
229
+ name: 'SmartTable',
230
+ controlId: '03',
231
+ children: [],
232
+ controlType: 'sap.ui.comp.smarttable.SmartTable',
233
+ editable: true,
234
+ visible: true
235
+ }
236
+ ];
237
+ const initialState: State = {
238
+ deviceType: DeviceType.Desktop,
239
+ scale: 1,
240
+ outline: model,
241
+ filterQuery: filterInitOptions,
242
+ selectedControl: undefined,
243
+ changes: {
244
+ pending: [],
245
+ saved: [],
246
+ controls: {}
247
+ },
248
+ icons: []
249
+ };
250
+ const { container } = render(<OutlinePanel />, { initialState });
251
+ const funnelIcon = container.querySelector('[data-icon-name="funnel"]') as Element;
252
+ // open callout
253
+ fireEvent.click(funnelIcon);
254
+ const focusCommonlyUsed = screen.getByText(/Show only commonly used/i);
255
+ // click on show hidden checkbox
256
+ fireEvent.click(focusCommonlyUsed);
257
+ // node 'one' and 'two' is filter out
258
+ const one = screen.queryByText(/one/i);
259
+ expect(one).toBeNull();
260
+ const two = screen.queryByText(/two/i);
261
+ expect(two).toBeNull();
262
+ // node 'SmartTable' exists in tree
263
+ const SmartTable = screen.getByText(/SmartTable/i);
264
+ expect(SmartTable).toBeInTheDocument();
265
+ });
266
+ test('do not expand to previously selected control', () => {
267
+ const { store, container } = render(<OutlinePanel />);
268
+ // clear default applied filters
269
+ const action = filterNodes([
270
+ { name: FilterName.focusEditable, value: false },
271
+ { name: FilterName.focusCommonlyUsed, value: false }
272
+ ]);
273
+ store.dispatch(action);
274
+ const model = getModel(true, false);
275
+ store.dispatch(outlineChanged(model));
276
+ const arrowRight = container.querySelector('[data-icon-name="Chevron"]') as Element;
277
+ // expand first node
278
+ fireEvent.click(arrowRight);
279
+ // select node
280
+ screen.getByText(/second child of one/i).click();
281
+ store.dispatch(controlSelected({ id: '01-02', type: '', properties: [], name: 'testing1' }));
282
+ // collapse first node
283
+ fireEvent.click(arrowRight);
284
+ // select outer node
285
+ screen.getByText(/two/i).click();
286
+ store.dispatch(controlSelected({ id: '02', type: '', properties: [], name: 'testing2' }));
287
+ expect(screen.queryByText(/second child of one/i)).toBeNull();
288
+ });
289
+
290
+ test('updateSelectionFromPreview', () => {
291
+ const { store, container } = render(<OutlinePanel />);
292
+ // clear default applied filters
293
+ const action = filterNodes([
294
+ { name: FilterName.focusEditable, value: true },
295
+ { name: FilterName.focusCommonlyUsed, value: false }
296
+ ]);
297
+ store.dispatch(action);
298
+ const model = getModel(true, false);
299
+ store.dispatch(outlineChanged(model));
300
+ const arrowRight = container.querySelector('[data-icon-name="Chevron"]') as Element;
301
+ // expand first node
302
+ fireEvent.click(arrowRight);
303
+
304
+ // select child node
305
+ screen.getByText(/second child of one/i).click();
306
+ let selectControl = { id: '01-03', type: '', properties: [], name: 'testing3' };
307
+ store.dispatch(controlSelected(selectControl));
308
+ let expectedControl = store.getState().selectedControl;
309
+ expect(expectedControl).toEqual(selectControl);
310
+
311
+ // select outer node
312
+ screen.getByText(/two/i).click();
313
+ selectControl = { id: '03', type: '', properties: [], name: 'testing4' };
314
+ store.dispatch(controlSelected(selectControl));
315
+ expectedControl = store.getState().selectedControl;
316
+ expect(expectedControl).toEqual(selectControl);
317
+ });
318
+
319
+ test('show change indicator', () => {
320
+ const initialState: State = {
321
+ deviceType: DeviceType.Desktop,
322
+ scale: 1,
323
+ outline: getModel(true, true, true, true),
324
+ filterQuery: filterInitOptions,
325
+ selectedControl: undefined,
326
+ changes: {
327
+ pending: [],
328
+ saved: [],
329
+ controls: {
330
+ '01': {
331
+ pending: 0,
332
+ saved: 1,
333
+ properties: {},
334
+ controlName: 'test01'
335
+ },
336
+ '01-01': {
337
+ pending: 0,
338
+ saved: 1,
339
+ properties: {},
340
+ controlName: 'test01-01'
341
+ }
342
+ }
343
+ },
344
+ icons: []
345
+ };
346
+ const { container } = render(<OutlinePanel />, { initialState });
347
+ const arrowRight = container.querySelector('[data-icon-name="Chevron"]') as Element;
348
+ expect(arrowRight).toBeInTheDocument();
349
+ fireEvent.click(arrowRight);
350
+ const indicator = container.querySelectorAll('svg circle');
351
+ expect(indicator).toHaveLength(2);
352
+ });
353
+ });
@@ -0,0 +1,36 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`utils getFilteredModel focus commonly used show 1`] = `
4
+ Array [
5
+ Object {
6
+ "children": Array [],
7
+ "controlId": "03",
8
+ "controlType": "sap.ui.comp.smarttable.SmartTable",
9
+ "editable": false,
10
+ "name": "SmartTable",
11
+ "visible": false,
12
+ },
13
+ ]
14
+ `;
15
+
16
+ exports[`utils getFilteredModel query query filter 1`] = `
17
+ Array [
18
+ Object {
19
+ "children": Array [
20
+ Object {
21
+ "children": Array [],
22
+ "controlId": "01-01",
23
+ "controlType": "name.space.first.child.one",
24
+ "editable": true,
25
+ "name": "first child of one",
26
+ "visible": true,
27
+ },
28
+ ],
29
+ "controlId": "01",
30
+ "controlType": "name.space.one",
31
+ "editable": true,
32
+ "name": "one",
33
+ "visible": true,
34
+ },
35
+ ]
36
+ `;
@@ -0,0 +1,83 @@
1
+ import type { OutlineNode } from '@sap-ux-private/control-property-editor-common';
2
+ import { getFilteredModel } from '../../../../src/panels/outline/utils';
3
+ import type { FilterOptions } from '../../../../src/slice';
4
+ import { FilterName } from '../../../../src/slice';
5
+ const getModel = (editable = true, visible = true, toggleParent = false, toggleChildren = false): OutlineNode[] => {
6
+ const model: OutlineNode[] = [
7
+ {
8
+ name: 'one',
9
+ controlId: '01',
10
+ children: [
11
+ {
12
+ name: 'first child of one',
13
+ controlId: '01-01',
14
+ children: [],
15
+ controlType: 'name.space.first.child.one',
16
+ editable: toggleChildren ? !toggleChildren : editable,
17
+ visible: toggleChildren ? !toggleChildren : visible
18
+ },
19
+ {
20
+ name: 'second child of one',
21
+ controlId: '01-02',
22
+ children: [],
23
+ controlType: 'name.space.second.child.one',
24
+ editable: toggleChildren ? !toggleChildren : editable,
25
+ visible: toggleChildren ? !toggleChildren : visible
26
+ }
27
+ ],
28
+ controlType: 'name.space.one',
29
+ editable: toggleParent ? !toggleParent : editable,
30
+ visible: toggleParent ? !toggleParent : visible
31
+ },
32
+ {
33
+ name: 'two',
34
+ controlId: '02',
35
+ children: [],
36
+ controlType: 'name.space.two',
37
+ editable: toggleParent ? !toggleParent : editable,
38
+ visible: toggleParent ? !toggleParent : visible
39
+ },
40
+ {
41
+ name: 'SmartTable',
42
+ controlId: '03',
43
+ children: [],
44
+ controlType: 'sap.ui.comp.smarttable.SmartTable',
45
+ editable: toggleParent ? !toggleParent : editable,
46
+ visible: toggleParent ? !toggleParent : visible
47
+ }
48
+ ];
49
+ return model;
50
+ };
51
+
52
+ describe('utils', () => {
53
+ describe('getFilteredModel', () => {
54
+ describe('query', () => {
55
+ test('no filter condition meet => model without filter is returned', () => {
56
+ const model = getModel();
57
+ const filterOptions: FilterOptions[] = [{ name: FilterName.query, value: '' }];
58
+ const result = getFilteredModel(model, filterOptions);
59
+ expect(result).toEqual(model);
60
+ });
61
+ test('query filter', () => {
62
+ const model = getModel();
63
+ const filterOptions: FilterOptions[] = [{ name: FilterName.query, value: 'first child' }];
64
+ const result = getFilteredModel(model, filterOptions);
65
+ expect(result).toMatchSnapshot();
66
+ });
67
+ });
68
+ describe('focus commonly used', () => {
69
+ test('no filter condition meet => model without filter is returned', () => {
70
+ const model = getModel();
71
+ const filterOptions: FilterOptions[] = [{ name: FilterName.focusCommonlyUsed, value: false }];
72
+ const result = getFilteredModel(model, filterOptions);
73
+ expect(result).toEqual(model);
74
+ });
75
+ test('show', () => {
76
+ const model = getModel(false, true, true, false);
77
+ const filterOptions: FilterOptions[] = [{ name: FilterName.focusCommonlyUsed, value: true }];
78
+ const result = getFilteredModel(model, filterOptions);
79
+ expect(result).toMatchSnapshot();
80
+ });
81
+ });
82
+ });
83
+ });
@@ -0,0 +1,18 @@
1
+ import { screen } from '@testing-library/react';
2
+ import { render } from '../../utils';
3
+ import React from 'react';
4
+ import type { ClipboardProps } from '../../../../src/panels/properties/Clipboard';
5
+ import { Clipboard } from '../../../../src/panels/properties/Clipboard';
6
+
7
+ describe('Clipboard', () => {
8
+ const clipboardProps: ClipboardProps = {
9
+ label: 'testLabel'
10
+ };
11
+
12
+ test('initial load', () => {
13
+ render(<Clipboard {...clipboardProps} />);
14
+
15
+ expect(screen.getByTestId('copied-to-clipboard-popup')).toBeInTheDocument();
16
+ expect(screen.getByTestId('copied-to-clipboard-message')).toBeInTheDocument();
17
+ });
18
+ });
@@ -0,0 +1,62 @@
1
+ import { cleanup, fireEvent, screen } from '@testing-library/react';
2
+ import React from 'react';
3
+ import type { StringControlPropertyWithOptions } from '@sap-ux-private/control-property-editor-common';
4
+ import { DROPDOWN_EDITOR_TYPE, STRING_VALUE_TYPE } from '@sap-ux-private/control-property-editor-common';
5
+ import { DropdownEditor, valueChanged } from '../../../../src/panels/properties/DropdownEditor';
6
+ import * as slice from '../../../../src/slice';
7
+ import '@testing-library/jest-dom';
8
+
9
+ import { render } from '../../utils';
10
+
11
+ describe('DropdownEditor', () => {
12
+ test('render & click (for boolean value)', async () => {
13
+ // arrange
14
+ const controlId = 'testControlId';
15
+ const propertyName = 'testProperty';
16
+ const value = 'option1';
17
+ const property: StringControlPropertyWithOptions = {
18
+ type: STRING_VALUE_TYPE,
19
+ editor: DROPDOWN_EDITOR_TYPE,
20
+ isEnabled: true,
21
+ name: propertyName,
22
+ value,
23
+ readableName: '',
24
+ options: [
25
+ { key: 'option1', text: 'option1' },
26
+ { key: 'option2', text: 'option2' }
27
+ ]
28
+ };
29
+ const testId = `${propertyName}--DropdownEditor`;
30
+ jest.spyOn(slice, 'changeProperty');
31
+
32
+ cleanup();
33
+
34
+ // act
35
+ const { dispatch } = render(<DropdownEditor property={property} controlId={controlId} controlName="Button" />);
36
+ const dropDownEditor = screen.getByTestId(testId);
37
+ const dropDownEditorInput = dropDownEditor.querySelector('input');
38
+ jest.spyOn(window, 'setTimeout').mockImplementation((cb: any) => {
39
+ cb(undefined, undefined, 'test');
40
+ });
41
+ if (dropDownEditorInput) {
42
+ fireEvent.focus(dropDownEditorInput);
43
+ fireEvent.input(dropDownEditorInput, { target: { value: 'test' } });
44
+ fireEvent.blur(dropDownEditorInput);
45
+ }
46
+ expect(dispatch).toBeCalledTimes(2);
47
+ });
48
+ test('valueChanged function', () => {
49
+ const result = valueChanged('testControlId', 'testPropertyName', 'newValue', 'Button');
50
+ expect(result).toMatchInlineSnapshot(`
51
+ Object {
52
+ "payload": Object {
53
+ "controlId": "testControlId",
54
+ "controlName": "Button",
55
+ "propertyName": "testPropertyName",
56
+ "value": "newValue",
57
+ },
58
+ "type": "app/change-property",
59
+ }
60
+ `);
61
+ });
62
+ });
@@ -0,0 +1,34 @@
1
+ import { screen, fireEvent } from '@testing-library/react';
2
+ import { render } from '../../utils';
3
+ import React from 'react';
4
+ import '@testing-library/jest-dom';
5
+ import { Funnel } from '../../../../src/panels/properties/Funnel';
6
+ import { filterNodes } from '../../../../src/slice';
7
+
8
+ describe('Funnel', () => {
9
+ test('initial load', () => {
10
+ render(<Funnel />);
11
+ const funnel = screen.getByRole('button');
12
+ expect(funnel).toBeInTheDocument();
13
+
14
+ fireEvent.click(funnel);
15
+
16
+ expect(screen.getByRole('checkbox')).toBeInTheDocument();
17
+ });
18
+
19
+ test('on filter option click', () => {
20
+ const { dispatch } = render(<Funnel />);
21
+ const funnel = screen.getByRole('button');
22
+ expect(funnel).toBeInTheDocument();
23
+
24
+ fireEvent.click(funnel);
25
+ const checkBox = screen.getByRole('checkbox');
26
+ expect(checkBox).toBeInTheDocument();
27
+ expect(checkBox).toBeChecked;
28
+
29
+ checkBox?.click();
30
+ expect(checkBox).not.toBeChecked;
31
+ expect(dispatch).toBeCalled();
32
+ expect(dispatch).toBeCalledWith(filterNodes([{ name: 'show-editable-properties' as any, value: false }]));
33
+ });
34
+ });
@@ -0,0 +1,36 @@
1
+ import { screen, fireEvent } from '@testing-library/react';
2
+ import { render } from '../../utils';
3
+ import React from 'react';
4
+ import type { HeaderFieldProps } from '../../../../src/panels/properties/HeaderField';
5
+ import { HeaderField } from '../../../../src/panels/properties/HeaderField';
6
+ import { initI18n } from '../../../../src/i18n';
7
+
8
+ describe('HeaderField', () => {
9
+ beforeAll(() => {
10
+ initI18n();
11
+ });
12
+ const headerFieldProps: HeaderFieldProps = {
13
+ label: 'testLabel',
14
+ documentation: {} as any,
15
+ value: 'testValue',
16
+ hidden: false
17
+ };
18
+
19
+ const writeTextMock = jest.fn();
20
+ Object.assign(global.navigator, {
21
+ clipboard: {
22
+ writeText: writeTextMock
23
+ }
24
+ });
25
+
26
+ test('initial load', () => {
27
+ render(<HeaderField {...headerFieldProps} />);
28
+ const copyButton = screen.getByRole('button');
29
+ expect(copyButton).toBeInTheDocument();
30
+
31
+ fireEvent.click(copyButton);
32
+
33
+ expect(screen.getByText('Copied to clipboard')).toBeInTheDocument();
34
+ expect(writeTextMock).toBeCalled();
35
+ });
36
+ });