@sap-ux/control-property-editor 0.3.1 → 0.4.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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "displayName": "Control Property Editor",
4
4
  "description": "Control Property Editor",
5
5
  "license": "Apache-2.0",
6
- "version": "0.3.1",
6
+ "version": "0.4.0",
7
7
  "main": "dist/app.js",
8
8
  "repository": {
9
9
  "type": "git",
@@ -50,7 +50,7 @@
50
50
  "postcss": "8.4.31",
51
51
  "yargs-parser": "21.1.1",
52
52
  "@sap-ux/ui-components": "1.11.21",
53
- "@sap-ux-private/control-property-editor-common": "0.2.0"
53
+ "@sap-ux-private/control-property-editor-common": "0.3.0"
54
54
  },
55
55
  "scripts": {
56
56
  "clean": "rimraf ./dist ./coverage *.tsbuildinfo",
@@ -64,5 +64,6 @@
64
64
  "CONFIRM_CHANGE_SUMMARY_DELETE_SUBTEXT": "Are you sure you want to delete the change for this property? This action cannot be undone.",
65
65
  "NO_CONTROL_CHANGES_FOUND": "No Control Changes Found",
66
66
  "CHANGE": "Change",
67
- "FILE": "File: "
67
+ "FILE": "File: ",
68
+ "CONTROL": "Selector Id: "
68
69
  }
@@ -1,12 +1,10 @@
1
1
  import type { ReactElement } from 'react';
2
2
  import React from 'react';
3
-
4
3
  import { Stack } from '@fluentui/react';
5
-
6
4
  import type { Change, ValidChange } from '@sap-ux-private/control-property-editor-common';
7
5
 
8
6
  import { Separator } from '../../components';
9
- import type { ControlGroupProps, ControlPropertyChange } from './ControlGroup';
7
+ import type { ControlGroupProps, ControlChange } from './ControlGroup';
10
8
  import { ControlGroup } from './ControlGroup';
11
9
  import type { UnknownChangeProps } from './UnknownChange';
12
10
  import { UnknownChange } from './UnknownChange';
@@ -87,19 +85,23 @@ function convertChanges(changes: Change[]): Item[] {
87
85
  const items: Item[] = [];
88
86
  let i = 0;
89
87
  while (i < changes.length) {
90
- const change = changes[i];
88
+ const change: Change = changes[i];
89
+ let group: ControlGroupProps;
91
90
  if (change.type === 'saved' && change.kind === 'unknown') {
92
91
  items.push({
93
92
  fileName: change.fileName,
94
- timestamp: change.timestamp
93
+ timestamp: change.timestamp,
94
+ header: true,
95
+ controlId: change.controlId ?? ''
95
96
  });
96
97
  i++;
97
98
  } else {
98
- const group: ControlGroupProps = {
99
+ group = {
99
100
  controlId: change.controlId,
101
+ controlName: change.controlName,
100
102
  text: convertCamelCaseToPascalCase(change.controlName),
101
103
  changeIndex: i,
102
- changes: [toPropertyChangeProps(change, i)]
104
+ changes: [classifyChange(change, i)]
103
105
  };
104
106
  items.push(group);
105
107
  i++;
@@ -112,7 +114,7 @@ function convertChanges(changes: Change[]): Item[] {
112
114
  ) {
113
115
  break;
114
116
  }
115
- group.changes.push(toPropertyChangeProps(nextChange, i));
117
+ group.changes.push(classifyChange(nextChange, i));
116
118
  i++;
117
119
  }
118
120
  }
@@ -121,21 +123,33 @@ function convertChanges(changes: Change[]): Item[] {
121
123
  }
122
124
 
123
125
  /**
124
- * Converts a change to ControlPropertyChange.
126
+ * Classify Change for grouping.
125
127
  *
126
128
  * @param change ValidChange
127
129
  * @param changeIndex number
128
- * @returns ControlPropertyChange
130
+ * @returns ControlChange
129
131
  */
130
- function toPropertyChangeProps(change: ValidChange, changeIndex: number): ControlPropertyChange {
131
- const { controlId, propertyName, value, controlName } = change;
132
- const base = {
133
- controlId,
134
- controlName,
135
- propertyName,
136
- value,
137
- changeIndex
138
- };
132
+ function classifyChange(change: ValidChange, changeIndex: number): ControlChange {
133
+ let base;
134
+ if (change.changeType === 'propertyChange' || change.changeType === 'propertyBindingChange') {
135
+ const { controlId, propertyName, value, controlName, changeType } = change;
136
+ base = {
137
+ controlId,
138
+ controlName,
139
+ propertyName,
140
+ value,
141
+ changeIndex,
142
+ changeType
143
+ };
144
+ } else {
145
+ const { controlId, controlName, changeType } = change;
146
+ base = {
147
+ controlId,
148
+ controlName,
149
+ changeIndex,
150
+ changeType
151
+ };
152
+ }
139
153
  if (change.type === 'pending') {
140
154
  const { isActive } = change;
141
155
  return {
@@ -154,28 +168,49 @@ function toPropertyChangeProps(change: ValidChange, changeIndex: number): Contro
154
168
  }
155
169
 
156
170
  /**
157
- * Returns true, if controlId is defined.
171
+ * Returns true, if controlName is defined.
158
172
  *
159
173
  * @param change ControlGroupProps | UnknownChangeProps
160
174
  * @returns boolean
161
175
  */
162
176
  export function isKnownChange(change: ControlGroupProps | UnknownChangeProps): change is ControlGroupProps {
163
- return (change as ControlGroupProps).controlId !== undefined;
177
+ return (change as ControlGroupProps).controlName !== undefined;
164
178
  }
165
179
 
166
- const filterPropertyChanges = (changes: ControlPropertyChange[], query: string): ControlPropertyChange[] => {
180
+ const filterPropertyChanges = (changes: ControlChange[], query: string): ControlChange[] => {
167
181
  return changes.filter((item) => {
168
- return (
169
- !query ||
170
- item.propertyName.trim().toLowerCase().includes(query) ||
171
- convertCamelCaseToPascalCase(item.propertyName.toString()).trim().toLowerCase().includes(query) ||
172
- item.value.toString().trim().toLowerCase().includes(query) ||
173
- convertCamelCaseToPascalCase(item.value.toString()).trim().toLowerCase().includes(query) ||
174
- (item.timestamp && getFormattedDateAndTime(item.timestamp).trim().toLowerCase().includes(query))
175
- );
182
+ if (item.propertyName) {
183
+ return (
184
+ !query ||
185
+ item.propertyName.trim().toLowerCase().includes(query) ||
186
+ convertCamelCaseToPascalCase(item.propertyName.toString()).trim().toLowerCase().includes(query) ||
187
+ item.value.toString().trim().toLowerCase().includes(query) ||
188
+ convertCamelCaseToPascalCase(item.value.toString()).trim().toLowerCase().includes(query) ||
189
+ (item.timestamp && getFormattedDateAndTime(item.timestamp).trim().toLowerCase().includes(query))
190
+ );
191
+ } else if (item.changeType) {
192
+ const changeType = convertCamelCaseToPascalCase(item.changeType);
193
+ return !query || changeType.trim().toLowerCase().includes(query);
194
+ }
176
195
  });
177
196
  };
178
197
 
198
+ const isQueryMatchesChange = (item: UnknownChangeProps, query: string): boolean => {
199
+ const parts = item.fileName.split('_');
200
+ const changeName = parts[parts.length - 1];
201
+ const name = convertCamelCaseToPascalCase(changeName + 'Change');
202
+ let dateTime = '';
203
+ if (item.timestamp) {
204
+ dateTime = getFormattedDateAndTime(item.timestamp).trim();
205
+ }
206
+ return (
207
+ !query ||
208
+ item.fileName.trim().toLowerCase().includes(query) ||
209
+ name.trim().toLowerCase().includes(query) ||
210
+ dateTime.toLowerCase().includes(query)
211
+ );
212
+ };
213
+
179
214
  /**
180
215
  * Filter group in change stack.
181
216
  *
@@ -191,6 +226,9 @@ function filterGroup(model: Item[], query: string): Item[] {
191
226
  for (const item of model) {
192
227
  let parentMatch = false;
193
228
  if (!isKnownChange(item)) {
229
+ if (isQueryMatchesChange(item, query)) {
230
+ filteredModel.push({ ...item, changes: [] });
231
+ }
194
232
  continue;
195
233
  }
196
234
  const name = item.text.trim().toLowerCase();
@@ -3,7 +3,6 @@
3
3
  margin: 17px 15px 10px 15px;
4
4
  flex-direction: row;
5
5
  align-items: center;
6
- overflow-y: auto;
7
6
  }
8
7
 
9
8
  .noData {
@@ -17,6 +17,22 @@ import { ChangeStackHeader } from './ChangeStackHeader';
17
17
 
18
18
  import styles from './ChangesPanel.module.scss';
19
19
 
20
+ export interface ChangeProps {
21
+ controlId: string;
22
+ controlName: string;
23
+ changeIndex: number;
24
+ changeType: string;
25
+ propertyName: string;
26
+ value: string | number | boolean;
27
+ isActive: boolean;
28
+ timestamp?: number;
29
+ fileName?: string;
30
+ /**
31
+ * Class used for showing and hiding actions
32
+ */
33
+ actionClassName: string;
34
+ }
35
+
20
36
  /**
21
37
  * React element for ChangePanel.
22
38
  *
@@ -5,18 +5,20 @@ import { Link, Stack } from '@fluentui/react';
5
5
  import { useAppDispatch } from '../../store';
6
6
  import { selectControl } from '@sap-ux-private/control-property-editor-common';
7
7
 
8
- import type { PropertyChangeProps } from './PropertyChange';
8
+ import type { ChangeProps } from './ChangesPanel';
9
9
  import { PropertyChange } from './PropertyChange';
10
+ import { OtherChange } from './OtherChange';
10
11
 
11
12
  import styles from './ControlGroup.module.scss';
12
13
 
13
14
  export interface ControlGroupProps {
14
15
  text: string;
15
16
  controlId: string;
17
+ controlName: string;
16
18
  changeIndex: number;
17
- changes: ControlPropertyChange[];
19
+ changes: ControlChange[];
18
20
  }
19
- export type ControlPropertyChange = Omit<PropertyChangeProps, 'actionClassName'>;
21
+ export type ControlChange = Omit<ChangeProps, 'actionClassName'>;
20
22
 
21
23
  /**
22
24
  * React Element for control groups.
@@ -50,10 +52,16 @@ export function ControlGroup(controlGroupProps: ControlGroupProps): ReactElement
50
52
  </Stack.Item>
51
53
  {changes.map((change) => (
52
54
  <Stack.Item
53
- data-testid={`${stackName}-${controlId}-${change.propertyName}-${change.changeIndex}`}
55
+ data-testid={`${stackName}-${controlId}-${change.propertyName ?? change.changeType}-${
56
+ change.changeIndex
57
+ }`}
54
58
  key={`${change.changeIndex}`}
55
59
  className={styles.item}>
56
- <PropertyChange {...change} actionClassName={styles.actions} />
60
+ {['propertyChange', 'propertyBindingChange'].includes(change.changeType) ? (
61
+ <PropertyChange {...change} actionClassName={styles.actions} />
62
+ ) : (
63
+ <OtherChange {...change} actionClassName={styles.actions} />
64
+ )}
57
65
  </Stack.Item>
58
66
  ))}
59
67
  </Stack>
@@ -0,0 +1,13 @@
1
+ .text {
2
+ color: var(--vscode-editor-foreground);
3
+ }
4
+
5
+ .text {
6
+ margin-right: 5px;
7
+ line-height: 18px;
8
+ display: inline-block;
9
+ }
10
+
11
+ .changeType {
12
+ overflow-wrap: anywhere;
13
+ }
@@ -0,0 +1,41 @@
1
+ import type { ReactElement } from 'react';
2
+ import React from 'react';
3
+
4
+ import { Stack, Text } from '@fluentui/react';
5
+ import { convertCamelCaseToPascalCase } from '@sap-ux-private/control-property-editor-common';
6
+
7
+ import styles from './OtherChange.module.scss';
8
+ import type { ChangeProps } from './ChangesPanel';
9
+
10
+ /**
11
+ * React element for Other change.
12
+ *
13
+ * @param OtherChangeProps OtherChangeProps
14
+ * @returns ReactElement
15
+ */
16
+ export function OtherChange(OtherChangeProps: Readonly<ChangeProps>): ReactElement {
17
+ const { changeType, isActive } = OtherChangeProps;
18
+ return (
19
+ <Stack
20
+ tokens={{
21
+ childrenGap: 5
22
+ }}
23
+ className={styles.container}
24
+ style={{
25
+ opacity: isActive ? 1 : 0.4
26
+ }}>
27
+ <Stack.Item className={styles.changeType}>
28
+ <Stack
29
+ horizontal
30
+ horizontalAlign={'space-between'}
31
+ tokens={{
32
+ childrenGap: 5
33
+ }}>
34
+ <Stack.Item>
35
+ <Text className={styles.text}>{convertCamelCaseToPascalCase(changeType)}</Text>
36
+ </Stack.Item>
37
+ </Stack>
38
+ </Stack.Item>
39
+ </Stack>
40
+ );
41
+ }
@@ -15,10 +15,6 @@
15
15
  opacity: 0.5;
16
16
  }
17
17
 
18
- .valueIcon svg path {
19
- fill: #ffffff !important;
20
- }
21
-
22
18
  .property {
23
19
  overflow-wrap: anywhere;
24
20
  }
@@ -12,21 +12,7 @@ import { IconName } from '../../icons';
12
12
 
13
13
  import styles from './PropertyChange.module.scss';
14
14
  import { getFormattedDateAndTime } from './utils';
15
-
16
- export interface PropertyChangeProps {
17
- controlId: string;
18
- controlName: string;
19
- changeIndex: number;
20
- propertyName: string;
21
- value: string | number | boolean;
22
- isActive: boolean;
23
- timestamp?: number;
24
- fileName?: string;
25
- /**
26
- * Class used for showing and hiding actions
27
- */
28
- actionClassName: string;
29
- }
15
+ import type { ChangeProps } from './ChangesPanel';
30
16
 
31
17
  /**
32
18
  * React element for property change.
@@ -34,7 +20,7 @@ export interface PropertyChangeProps {
34
20
  * @param propertyChangeProps PropertyChangeProps
35
21
  * @returns ReactElement
36
22
  */
37
- export function PropertyChange(propertyChangeProps: PropertyChangeProps): ReactElement {
23
+ export function PropertyChange(propertyChangeProps: Readonly<ChangeProps>): ReactElement {
38
24
  const { controlId, propertyName, value, isActive, timestamp, fileName, actionClassName } = propertyChangeProps;
39
25
  const { t } = useTranslation();
40
26
  const dispatch = useDispatch();
@@ -9,8 +9,43 @@
9
9
  white-space: nowrap;
10
10
  color: var(--vscode-editor-foreground);
11
11
  width: 240px;
12
+ direction: rtl;
13
+ text-align: left;
14
+ }
15
+ .fileLabel {
16
+ min-width: 30px;
17
+ margin-top: 5px;
18
+ }
19
+ .fileText {
20
+ margin-top: 5px;
21
+ line-height: 18px;
22
+ display: inline-block;
23
+ text-overflow: ellipsis;
24
+ overflow: hidden;
25
+ white-space: nowrap;
26
+ color: var(--vscode-editor-foreground);
27
+ width: 210px;
28
+ direction: rtl;
29
+ text-align: left;
30
+ }
31
+ .controlLabel {
32
+ min-width: 75px;
33
+ margin-top: 5px;
34
+ }
35
+ .controlText {
36
+ margin-top: 5px;
37
+ line-height: 18px;
38
+ display: inline-block;
39
+ text-overflow: ellipsis;
40
+ overflow: hidden;
41
+ white-space: nowrap;
42
+ color: var(--vscode-editor-foreground);
43
+ width: 165px;
44
+ direction: rtl;
45
+ text-align: left;
12
46
  }
13
47
  .timestamp {
48
+ margin-top: 5px;
14
49
  color: var(--vscode-editor-foreground);
15
50
  font-size: 11px;
16
51
  line-height: 15px;
@@ -19,7 +54,7 @@
19
54
  }
20
55
  .textHeader {
21
56
  display: inline-block;
22
- color: var(--vscode-textLink-foreground);
57
+ color: var(--vscode-foreground);
23
58
  font-size: 13px;
24
59
  font-weight: bold;
25
60
  text-overflow: ellipsis;
@@ -12,6 +12,8 @@ import { getFormattedDateAndTime } from './utils';
12
12
  export interface UnknownChangeProps {
13
13
  fileName: string;
14
14
  timestamp?: number;
15
+ controlId?: string;
16
+ header?: boolean;
15
17
  }
16
18
 
17
19
  /**
@@ -21,7 +23,7 @@ export interface UnknownChangeProps {
21
23
  * @returns ReactElement
22
24
  */
23
25
  export function UnknownChange(unknownChangeProps: UnknownChangeProps): ReactElement {
24
- const { fileName, timestamp } = unknownChangeProps;
26
+ const { fileName, timestamp, header, controlId } = unknownChangeProps;
25
27
  const { t } = useTranslation();
26
28
  const dispatch = useDispatch();
27
29
  const [dialogState, setDialogState] = useState<PropertyChangeDeletionDetails | undefined>(undefined);
@@ -46,14 +48,27 @@ export function UnknownChange(unknownChangeProps: UnknownChangeProps): ReactElem
46
48
  <Stack.Item className={styles.property}>
47
49
  <Stack horizontal>
48
50
  <Stack.Item>
49
- <Text className={styles.textHeader}>
50
- {name} {t('CHANGE')}
51
- </Text>
52
- <Text className={styles.text} title={fileName}>
53
- {t('FILE')}
54
- {fileName}
55
- </Text>
51
+ {header && (
52
+ <Text className={styles.textHeader}>
53
+ {name} {t('CHANGE')}
54
+ </Text>
55
+ )}
56
+ <Stack horizontal>
57
+ <Stack.Item className={styles.fileLabel}>{t('FILE')}</Stack.Item>
58
+ <Stack.Item className={styles.fileText} title={fileName}>
59
+ {fileName}
60
+ </Stack.Item>
61
+ </Stack>
62
+ {controlId && (
63
+ <Stack horizontal>
64
+ <Stack.Item className={styles.controlLabel}>{t('CONTROL')}</Stack.Item>
65
+ <Stack.Item className={styles.controlText} title={controlId}>
66
+ {controlId}
67
+ </Stack.Item>
68
+ </Stack>
69
+ )}
56
70
  </Stack.Item>
71
+
57
72
  {fileName && (
58
73
  <Stack.Item className={styles.actions}>
59
74
  <UIIconButton
@@ -1,3 +1,3 @@
1
1
  export { isKnownChange } from './ChangeStack';
2
2
  export { ChangesPanel } from './ChangesPanel';
3
- export { ControlGroupProps, ControlPropertyChange } from './ControlGroup';
3
+ export { ControlGroupProps, ControlChange } from './ControlGroup';
@@ -6,11 +6,7 @@ import { Text, Stack } from '@fluentui/react';
6
6
 
7
7
  import { UIIcon, UIIconButton, UiIcons } from '@sap-ux/ui-components';
8
8
 
9
- import type {
10
- Control,
11
- SavedPropertyChange,
12
- PendingPropertyChange
13
- } from '@sap-ux-private/control-property-editor-common';
9
+ import type { Control, SavedPropertyChange, PendingChange } from '@sap-ux-private/control-property-editor-common';
14
10
  import { Separator } from '../../components';
15
11
  import type { RootState } from '../../store';
16
12
 
@@ -42,7 +38,7 @@ export function PropertyDocumentation(propDocProps: PropertyDocumentationProps):
42
38
  pending: number;
43
39
  saved: number;
44
40
  lastSavedChange?: SavedPropertyChange;
45
- lastChange?: PendingPropertyChange;
41
+ lastChange?: PendingChange;
46
42
  }
47
43
  | undefined
48
44
  >((state) => state.changes.controls[control?.id ?? '']?.properties[propertyName]);