@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.
- package/.eslintignore +1 -0
- package/.eslintrc.js +16 -0
- package/CHANGELOG.md +7 -0
- package/LICENSE +201 -0
- package/README.md +16 -0
- package/dist/app.css +2 -0
- package/dist/app.css.map +7 -0
- package/dist/app.js +347 -0
- package/dist/app.js.map +7 -0
- package/esbuild.js +25 -0
- package/jest.config.js +20 -0
- package/package.json +68 -0
- package/src/App.scss +57 -0
- package/src/App.tsx +136 -0
- package/src/Workarounds.scss +79 -0
- package/src/actions.ts +3 -0
- package/src/components/AppLogo.module.scss +8 -0
- package/src/components/AppLogo.tsx +75 -0
- package/src/components/ChangeIndicator.tsx +80 -0
- package/src/components/Separator.tsx +32 -0
- package/src/components/ThemeSelectorCallout.scss +48 -0
- package/src/components/ThemeSelectorCallout.tsx +125 -0
- package/src/components/ToolBar.scss +39 -0
- package/src/components/ToolBar.tsx +26 -0
- package/src/components/index.ts +4 -0
- package/src/devices.ts +18 -0
- package/src/global.d.ts +4 -0
- package/src/i18n/i18n.json +68 -0
- package/src/i18n.ts +25 -0
- package/src/icons.tsx +198 -0
- package/src/index.css +1288 -0
- package/src/index.tsx +47 -0
- package/src/middleware.ts +54 -0
- package/src/panels/LeftPanel.scss +17 -0
- package/src/panels/LeftPanel.tsx +48 -0
- package/src/panels/changes/ChangeStack.module.scss +3 -0
- package/src/panels/changes/ChangeStack.tsx +219 -0
- package/src/panels/changes/ChangeStackHeader.tsx +43 -0
- package/src/panels/changes/ChangesPanel.module.scss +18 -0
- package/src/panels/changes/ChangesPanel.tsx +90 -0
- package/src/panels/changes/ControlGroup.module.scss +17 -0
- package/src/panels/changes/ControlGroup.tsx +61 -0
- package/src/panels/changes/PropertyChange.module.scss +24 -0
- package/src/panels/changes/PropertyChange.tsx +159 -0
- package/src/panels/changes/UnknownChange.module.scss +46 -0
- package/src/panels/changes/UnknownChange.tsx +96 -0
- package/src/panels/changes/index.tsx +3 -0
- package/src/panels/changes/utils.ts +36 -0
- package/src/panels/index.ts +2 -0
- package/src/panels/outline/Funnel.tsx +64 -0
- package/src/panels/outline/NoControlFound.tsx +45 -0
- package/src/panels/outline/OutlinePanel.scss +98 -0
- package/src/panels/outline/OutlinePanel.tsx +38 -0
- package/src/panels/outline/Tree.tsx +393 -0
- package/src/panels/outline/index.ts +1 -0
- package/src/panels/outline/utils.ts +154 -0
- package/src/panels/properties/Clipboard.tsx +44 -0
- package/src/panels/properties/DeviceSelector.tsx +40 -0
- package/src/panels/properties/DeviceToggle.tsx +39 -0
- package/src/panels/properties/DropdownEditor.tsx +80 -0
- package/src/panels/properties/Funnel.tsx +64 -0
- package/src/panels/properties/HeaderField.tsx +150 -0
- package/src/panels/properties/IconValueHelp.tsx +203 -0
- package/src/panels/properties/InputTypeSelector.tsx +20 -0
- package/src/panels/properties/InputTypeToggle.module.scss +4 -0
- package/src/panels/properties/InputTypeToggle.tsx +79 -0
- package/src/panels/properties/InputTypeWrapper.tsx +259 -0
- package/src/panels/properties/NoControlSelected.tsx +38 -0
- package/src/panels/properties/Properties.scss +102 -0
- package/src/panels/properties/PropertiesList.tsx +162 -0
- package/src/panels/properties/PropertiesPanel.tsx +30 -0
- package/src/panels/properties/PropertyDocumentation.module.scss +81 -0
- package/src/panels/properties/PropertyDocumentation.tsx +174 -0
- package/src/panels/properties/SapUiIcon.scss +109 -0
- package/src/panels/properties/StringEditor.tsx +122 -0
- package/src/panels/properties/ViewChanger.module.scss +5 -0
- package/src/panels/properties/ViewChanger.tsx +143 -0
- package/src/panels/properties/constants.ts +2 -0
- package/src/panels/properties/index.tsx +1 -0
- package/src/panels/properties/propertyValuesCache.ts +39 -0
- package/src/panels/properties/types.ts +49 -0
- package/src/slice.ts +216 -0
- package/src/store.ts +19 -0
- package/src/use-local-storage.ts +40 -0
- package/src/use-window-size.ts +39 -0
- package/src/variables.scss +2 -0
- package/test/unit/App.test.tsx +207 -0
- package/test/unit/appIndex.test.ts +23 -0
- package/test/unit/components/ChangeIndicator.test.tsx +120 -0
- package/test/unit/components/ThemeSelector.test.tsx +41 -0
- package/test/unit/middleware.test.ts +116 -0
- package/test/unit/panels/changes/ChangesPanel.test.tsx +261 -0
- package/test/unit/panels/changes/utils.test.ts +40 -0
- package/test/unit/panels/outline/OutlinePanel.test.tsx +353 -0
- package/test/unit/panels/outline/__snapshots__/utils.test.ts.snap +36 -0
- package/test/unit/panels/outline/utils.test.ts +83 -0
- package/test/unit/panels/properties/Clipboard.test.tsx +18 -0
- package/test/unit/panels/properties/DropdownEditor.test.tsx +62 -0
- package/test/unit/panels/properties/Funnel.test.tsx +34 -0
- package/test/unit/panels/properties/HeaderField.test.tsx +36 -0
- package/test/unit/panels/properties/IconValueHelp.test.tsx +60 -0
- package/test/unit/panels/properties/InputTypeToggle.test.tsx +126 -0
- package/test/unit/panels/properties/InputTypeWrapper.test.tsx +430 -0
- package/test/unit/panels/properties/PropertyDocumentation.test.tsx +131 -0
- package/test/unit/panels/properties/StringEditor.test.tsx +107 -0
- package/test/unit/panels/properties/ViewChanger.test.tsx +190 -0
- package/test/unit/panels/properties/propertyValuesCache.test.ts +23 -0
- package/test/unit/slice.test.ts +268 -0
- package/test/unit/utils.tsx +67 -0
- package/test/utils/utils.tsx +25 -0
- package/tsconfig.eslint.json +4 -0
- 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
|
+
});
|