@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,268 @@
1
+ import {
2
+ changeStackModified,
3
+ propertyChanged,
4
+ propertyChangeFailed
5
+ } from '@sap-ux-private/control-property-editor-common';
6
+ import reducer, { FilterName, filterNodes, changeProperty } from '../../src/slice';
7
+
8
+ describe('main redux slice', () => {
9
+ describe('property changed', () => {
10
+ test('existing property', () => {
11
+ expect(
12
+ reducer(
13
+ {
14
+ selectedControl: {
15
+ id: 'control1',
16
+ properties: [
17
+ {
18
+ name: 'text',
19
+ value: 'old value'
20
+ }
21
+ ]
22
+ }
23
+ } as any,
24
+ propertyChanged({
25
+ controlId: 'control1',
26
+ propertyName: 'text',
27
+ newValue: 'new text'
28
+ })
29
+ )
30
+ ).toStrictEqual({
31
+ selectedControl: {
32
+ id: 'control1',
33
+ properties: [
34
+ {
35
+ name: 'text',
36
+ value: 'new text'
37
+ }
38
+ ]
39
+ }
40
+ });
41
+ });
42
+
43
+ test('filterNodes', () => {
44
+ expect(
45
+ reducer(
46
+ {
47
+ filterQuery: [
48
+ { name: FilterName.focusEditable, value: false },
49
+ { name: FilterName.focusCommonlyUsed, value: false },
50
+ { name: FilterName.query, value: '' }
51
+ ]
52
+ } as any,
53
+ filterNodes([
54
+ { name: FilterName.focusEditable, value: false },
55
+ { name: FilterName.focusCommonlyUsed, value: false }
56
+ ])
57
+ )
58
+ ).toStrictEqual({
59
+ filterQuery: [
60
+ {
61
+ name: 'focus-editable-controls',
62
+ value: false
63
+ },
64
+ {
65
+ name: 'focus-commonly-used-controls',
66
+ value: false
67
+ },
68
+ {
69
+ name: 'query',
70
+ value: ''
71
+ }
72
+ ]
73
+ });
74
+ });
75
+
76
+ test('propertyChangeFailed', () => {
77
+ expect(
78
+ reducer(
79
+ {
80
+ selectedControl: {
81
+ id: 'control1',
82
+ properties: [
83
+ {
84
+ name: 'text',
85
+ value: 'old value'
86
+ }
87
+ ]
88
+ }
89
+ } as any,
90
+ propertyChangeFailed({
91
+ controlId: 'control1',
92
+ propertyName: 'text',
93
+ errorMessage: 'change failed'
94
+ })
95
+ )
96
+ ).toStrictEqual({
97
+ selectedControl: {
98
+ id: 'control1',
99
+ properties: [{ errorMessage: 'change failed', name: 'text', value: 'old value' }]
100
+ }
101
+ });
102
+ });
103
+
104
+ test('changeProperty', () => {
105
+ expect(
106
+ reducer(
107
+ {
108
+ selectedControl: {
109
+ id: 'control1',
110
+ type: 'string',
111
+ properties: [
112
+ {
113
+ name: 'text',
114
+ value: 'old value'
115
+ }
116
+ ]
117
+ }
118
+ } as any,
119
+ changeProperty({
120
+ controlId: 'control1',
121
+ controlName: 'Button',
122
+ propertyName: 'text',
123
+ value: 'change text'
124
+ })
125
+ )
126
+ ).toStrictEqual({
127
+ selectedControl: {
128
+ id: 'control1',
129
+ properties: [
130
+ {
131
+ errorMessage: '',
132
+ name: 'text',
133
+ value: 'change text'
134
+ }
135
+ ],
136
+ type: 'string'
137
+ }
138
+ });
139
+ });
140
+
141
+ test('non existing property', () => {
142
+ expect(
143
+ reducer(
144
+ {
145
+ selectedControl: {
146
+ id: 'control1',
147
+ properties: [
148
+ {
149
+ name: 'text',
150
+ value: 'old value'
151
+ }
152
+ ]
153
+ }
154
+ } as any,
155
+ propertyChanged({
156
+ controlId: 'control1',
157
+ propertyName: 'does not exist',
158
+ newValue: 'new text'
159
+ })
160
+ )
161
+ ).toStrictEqual({
162
+ selectedControl: {
163
+ id: 'control1',
164
+ properties: [
165
+ {
166
+ name: 'text',
167
+ value: 'old value'
168
+ }
169
+ ]
170
+ }
171
+ });
172
+ });
173
+
174
+ test('changeStackModified', () => {
175
+ expect(
176
+ reducer(
177
+ {
178
+ changes: {
179
+ saved: [],
180
+ pending: [],
181
+ controls: [] // make sure that old value is not reused
182
+ }
183
+ } as any,
184
+ changeStackModified({
185
+ pending: [
186
+ {
187
+ type: 'pending',
188
+ controlName: 'Button',
189
+ controlId: 'control1',
190
+ isActive: true,
191
+ propertyName: 'text',
192
+ value: '{i18n>DELETE}'
193
+ }
194
+ ],
195
+ saved: [
196
+ {
197
+ controlId: 'control1',
198
+ controlName: 'Button',
199
+ propertyName: 'text',
200
+ type: 'saved',
201
+ kind: 'valid',
202
+ fileName: 'file',
203
+ timestamp: 123,
204
+ value: 'abc'
205
+ }
206
+ ]
207
+ })
208
+ )
209
+ ).toStrictEqual({
210
+ changes: {
211
+ controls: {
212
+ control1: {
213
+ controlName: 'Button',
214
+ pending: 1,
215
+ saved: 1,
216
+ properties: {
217
+ text: {
218
+ lastChange: {
219
+ controlName: 'Button',
220
+ controlId: 'control1',
221
+ isActive: true,
222
+ propertyName: 'text',
223
+ type: 'pending',
224
+ value: '{i18n>DELETE}'
225
+ },
226
+ lastSavedChange: {
227
+ controlId: 'control1',
228
+ controlName: 'Button',
229
+ kind: 'valid',
230
+ fileName: 'file',
231
+ propertyName: 'text',
232
+ timestamp: 123,
233
+ type: 'saved',
234
+ value: 'abc'
235
+ },
236
+ pending: 1,
237
+ saved: 1
238
+ }
239
+ }
240
+ }
241
+ },
242
+ pending: [
243
+ {
244
+ type: 'pending',
245
+ controlName: 'Button',
246
+ controlId: 'control1',
247
+ isActive: true,
248
+ propertyName: 'text',
249
+ value: '{i18n>DELETE}'
250
+ }
251
+ ],
252
+ saved: [
253
+ {
254
+ controlId: 'control1',
255
+ controlName: 'Button',
256
+ propertyName: 'text',
257
+ type: 'saved',
258
+ kind: 'valid',
259
+ fileName: 'file',
260
+ timestamp: 123,
261
+ value: 'abc'
262
+ }
263
+ ]
264
+ }
265
+ });
266
+ });
267
+ });
268
+ });
@@ -0,0 +1,67 @@
1
+ import type { PropsWithChildren, ReactElement } from 'react';
2
+ import React from 'react';
3
+ import '@testing-library/jest-dom';
4
+ import type { Store, Dispatch } from '@reduxjs/toolkit';
5
+ import { createStore } from '@reduxjs/toolkit';
6
+ import type { RenderOptions, RenderResult } from '@testing-library/react';
7
+ import { render as rtlRender } from '@testing-library/react';
8
+ import { Provider } from 'react-redux';
9
+
10
+ import reducer from '../../src/slice';
11
+
12
+ export type State = ReturnType<typeof reducer>;
13
+
14
+ export interface Options<T = State> extends RenderOptions {
15
+ initialState?: T;
16
+ store?: Store<State>;
17
+ }
18
+
19
+ export function render<T = State>(
20
+ ui: ReactElement,
21
+ { initialState, store = createStore(reducer, initialState as unknown as State), ...renderOptions }: Options<T> = {}
22
+ ): RenderResult & { store: Store; dispatch: jest.SpyInstance<Dispatch> } {
23
+ function Wrapper({ children }: PropsWithChildren<{}>): ReactElement {
24
+ return <Provider store={store}>{children}</Provider>;
25
+ }
26
+ const origDispatch = store.dispatch;
27
+ const dispatch = jest.fn(origDispatch);
28
+ store.dispatch = dispatch;
29
+ return { ...rtlRender(ui, { wrapper: Wrapper, ...renderOptions }), store, dispatch };
30
+ }
31
+
32
+ export const mockDomEventListener = (handler: Document | Window | Element = document): DOMEventListenerMock => {
33
+ const domEventListeners: { [k: string]: Array<Function> } = {};
34
+ // Mock for add event listener
35
+ handler.addEventListener = jest.fn((event, cb) => {
36
+ if (!domEventListeners[event]) {
37
+ domEventListeners[event] = [];
38
+ }
39
+ domEventListeners[event].push(cb as Function);
40
+ });
41
+ handler.removeEventListener = jest.fn((event, cb) => {
42
+ if (domEventListeners[event]) {
43
+ const index = domEventListeners[event].findIndex((storedCb) => storedCb === cb);
44
+ if (index !== -1) {
45
+ domEventListeners[event].splice(index, 1);
46
+ }
47
+ if (domEventListeners[event].length === 0) {
48
+ delete domEventListeners[event];
49
+ }
50
+ }
51
+ });
52
+ return {
53
+ simulateEvent: (name: string, value: object): void => {
54
+ if (domEventListeners[name]) {
55
+ for (const cb of domEventListeners[name]) {
56
+ cb(value);
57
+ }
58
+ }
59
+ },
60
+ cleanDomEventListeners: (): void => {
61
+ for (const eventName in domEventListeners) {
62
+ delete domEventListeners[eventName];
63
+ }
64
+ },
65
+ domEventListeners
66
+ };
67
+ };
@@ -0,0 +1,25 @@
1
+ declare global {
2
+ interface Window {
3
+ ResizeObserver: any;
4
+ resizeobserversMocks: Array<any>;
5
+ }
6
+ }
7
+
8
+ export const mockResizeObserver = (): void => {
9
+ window.resizeobserversMocks = [];
10
+ window.ResizeObserver = jest.fn((callback: () => void) => {
11
+ const resizeObserver = {
12
+ observe: jest.fn(),
13
+ unobserve: jest.fn(),
14
+ disconnect: () => {
15
+ const index = window.resizeobserversMocks.indexOf(resizeObserver);
16
+ window.resizeobserversMocks.splice(index, 1);
17
+ },
18
+ simulate: () => {
19
+ callback();
20
+ }
21
+ };
22
+ window.resizeobserversMocks.push(resizeObserver);
23
+ return resizeObserver;
24
+ });
25
+ };
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "include": ["src", "test", ".eslintrc.js"]
4
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "jsx": "react",
5
+ "incremental": true,
6
+ "lib": [
7
+ "ES2021",
8
+ "dom"
9
+ ],
10
+ "strictNullChecks": true,
11
+ "outDir": "dist",
12
+ "module": "ES6",
13
+ "rootDir": ".",
14
+ "baseUrl": ".",
15
+ "types": [
16
+ "node",
17
+ "jest"
18
+ ]
19
+ },
20
+ "include": [
21
+ "src/**/*.ts",
22
+ "src/**/*.tsx",
23
+ "test/**/*.ts",
24
+ "test/**/*.tsx"
25
+ ],
26
+ "exclude": [
27
+ "dist",
28
+ "node_modules",
29
+ "coverage"
30
+ ],
31
+ "references": [
32
+ {
33
+ "path": "../control-property-editor-common"
34
+ },
35
+ {
36
+ "path": "../ui-components"
37
+ }
38
+ ]
39
+ }