Skip to content

fix: Add new generic change type and generic component for UI display #3034

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions packages/control-property-editor-common/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export const SAVED_CHANGE_TYPE = 'saved';
export const PROPERTY_CHANGE_KIND = 'property';
export const CONFIGURATION_CHANGE_KIND = 'configuration';
export const UNKNOWN_CHANGE_KIND = 'unknown';
export const GENERIC_CHANGE_KIND = 'generic';
export const CONTROL_CHANGE_KIND = 'control';
export interface PendingPropertyChange<T extends PropertyValue = PropertyValue> extends PropertyChange<T> {
type: typeof PENDING_CHANGE_TYPE;
Expand Down Expand Up @@ -179,8 +180,14 @@ export type PendingChange =
| PendingPropertyChange
| PendingOtherChange
| PendingControlChange
| PendingConfigurationChange;
export type SavedChange = SavedPropertyChange | SavedControlChange | UnknownSavedChange | SavedConfigurationChange;
| PendingConfigurationChange
| PendingGenericChange;
export type SavedChange =
| SavedPropertyChange
| SavedControlChange
| UnknownSavedChange
| SavedConfigurationChange
| SavedGenericChange;

export interface SavedPropertyChange<T extends PropertyValue = PropertyValue> extends PropertyChange<T> {
type: typeof SAVED_CHANGE_TYPE;
Expand All @@ -206,6 +213,34 @@ export interface UnknownSavedChange {
timestamp?: number;
}

export interface PendingGenericChange {
type: typeof PENDING_CHANGE_TYPE;
kind: typeof GENERIC_CHANGE_KIND;
title: string;
isActive: boolean;
fileName: string;
changeType: string;
controlId?: string;
genericProps: {
label: string;
value: string;
}[];
}

export interface SavedGenericChange {
type: typeof SAVED_CHANGE_TYPE;
kind: typeof GENERIC_CHANGE_KIND;
timestamp: number;
fileName: string;
title: string;
controlId?: string;
changeType: string;
genericProps: {
label: string;
value: string;
}[];
}

export interface SavedControlChange {
type: typeof SAVED_CHANGE_TYPE;
kind: typeof CONTROL_CHANGE_KIND;
Expand Down
122 changes: 109 additions & 13 deletions packages/control-property-editor/src/panels/changes/ChangeStack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ import React from 'react';
import { Stack } from '@fluentui/react';
import type {
Change,
PendingConfigurationChange,
PendingControlChange,
PendingGenericChange,
PendingPropertyChange,
SavedConfigurationChange,
SavedControlChange,
SavedGenericChange,
SavedPropertyChange
} from '@sap-ux-private/control-property-editor-common';
import {
Expand All @@ -15,7 +19,8 @@ import {
PROPERTY_CHANGE_KIND,
SAVED_CHANGE_TYPE,
UNKNOWN_CHANGE_KIND,
CONFIGURATION_CHANGE_KIND
CONFIGURATION_CHANGE_KIND,
GENERIC_CHANGE_KIND
} from '@sap-ux-private/control-property-editor-common';

import { Separator } from '../../components';
Expand All @@ -34,6 +39,7 @@ import type { ControlItemProps } from './ControlChange';
import { ControlChange } from './ControlChange';
import type { ConfigGroupProps } from './ConfigGroup';
import { ConifgGroup } from './ConfigGroup';
import { GenericGroup, type GenericGroupProps } from './GenericGroup';

export interface ChangeStackProps {
changes: Change[];
Expand Down Expand Up @@ -85,6 +91,12 @@ function renderChangeItem(item: Item, stackName: string): ReactElement {
<ControlGroup {...item} />
</Stack.Item>
);
} else if (isGenericChangeGroup(item)) {
return (
<Stack.Item key={`${stackName}-${item.text}-${item.index}`}>
<GenericGroup {...item} />
</Stack.Item>
);
} else if (isControlItem(item)) {
return (
<Stack.Item key={`${item.fileName}`}>
Expand Down Expand Up @@ -125,7 +137,27 @@ function getKey(i: number): string {
return `${i}-separator`;
}

type Item = ControlGroupProps | UnknownChangeProps | ControlItemProps | ConfigGroupProps;
type Item = ControlGroupProps | UnknownChangeProps | ControlItemProps | ConfigGroupProps | GenericGroupProps;

/**
* Test change is generic type.
*
* @param change supported change types
* @returns change is PendingGenericChange | SavedGenericChange
*/
function isGenericChange(change: Change): change is PendingGenericChange | SavedGenericChange {
return change.kind === 'generic';
}

/**
* Test change is configuration type.
*
* @param change supported change types
* @returns change is PendingConfigurationChange | SavedConfigurationChange
*/
function isConfigurationChange(change: Change): change is PendingConfigurationChange | SavedConfigurationChange {
return change.kind === CONFIGURATION_CHANGE_KIND;
}

/**
* Converts an array of changes into an array of items, grouping changes by controlId and handling different kinds of changes.
Expand All @@ -140,13 +172,14 @@ function convertChanges(changes: Change[]): Item[] {
const change: Change = changes[i];
let group: ControlGroupProps;
let configGroup: ConfigGroupProps;
let genericGroup: GenericGroupProps;
if (change.kind === UNKNOWN_CHANGE_KIND) {
items.push(handleUnknownChange(change));
i++;
} else if (change.kind === CONTROL_CHANGE_KIND) {
items.push(handleControlChange(change));
i++;
} else if (change.kind === CONFIGURATION_CHANGE_KIND) {
} else if (isConfigurationChange(change)) {
configGroup = {
text: convertCamelCaseToPascalCase(change.kind),
configPath: change.propertyPath,
Expand All @@ -158,15 +191,31 @@ function convertChanges(changes: Change[]): Item[] {
while (i < changes.length) {
// We don't need to add header again if the next control is the same
const nextChange = changes[i];
if (!isConfigurationChange(nextChange) || change.propertyPath !== nextChange?.propertyPath) {
break;
}
configGroup.changes.push(nextChange);
i++;
}
} else if (isGenericChange(change)) {
genericGroup = handleGenericGroupeChange(change, i);
items.push(genericGroup);
i++;
while (i < changes.length) {
// We don't need to add header again if the next title is the same
const nextChange = changes[i];
if (
nextChange.kind === UNKNOWN_CHANGE_KIND ||
nextChange.kind === PROPERTY_CHANGE_KIND ||
nextChange.kind === CONTROL_CHANGE_KIND ||
change.propertyPath !== nextChange?.propertyPath
!isGenericChange(nextChange) ||
(isGenericChange(nextChange) &&
(nextChange?.title !== change?.title ||
(nextChange?.title === change?.title &&
nextChange?.controlId &&
change?.controlId &&
nextChange?.controlId != change?.controlId)))
) {
break;
}
configGroup.changes.push(nextChange);
genericGroup.changes.push(nextChange);
i++;
}
} else {
Expand All @@ -180,6 +229,7 @@ function convertChanges(changes: Change[]): Item[] {
nextChange.kind === UNKNOWN_CHANGE_KIND ||
nextChange.kind === CONTROL_CHANGE_KIND ||
nextChange.kind === CONFIGURATION_CHANGE_KIND ||
nextChange.kind === GENERIC_CHANGE_KIND ||
change.controlId !== nextChange.controlId
) {
break;
Expand Down Expand Up @@ -246,13 +296,33 @@ function handleGroupedChange(change: PendingPropertyChange | SavedPropertyChange
};
}

/**
* Handles grouped changes by initializing a control group with a list of changes that share the same controlId.
*
* @param {Change} change - The initial change object to start the group.
* @param {number} i - The index of the initial change in the list.
* @returns {ControlGroupProps} A control group object containing grouped changes.
*/
function handleGenericGroupeChange(change: SavedGenericChange | PendingGenericChange, i: number): GenericGroupProps {
return {
text: String(change.title),
...(change.controlId && { controlId: change.controlId }),
kind: 'generic',
index: i,
changes: [change],
timestamp: change.type === SAVED_CHANGE_TYPE ? change.timestamp : undefined
};
}

/**
* Checks if item is of type {@link ControlGroupProps}.
*
* @param item ControlGroupProps | UnknownChangeProps | ControlItemProps
* @returns boolean
*/
function isPropertyGroup(item: ControlGroupProps | UnknownChangeProps | ControlItemProps): item is ControlGroupProps {
function isPropertyGroup(
item: ControlGroupProps | UnknownChangeProps | ControlItemProps | GenericGroupProps
): item is ControlGroupProps {
return (item as ControlGroupProps).controlName !== undefined;
}

Expand All @@ -268,7 +338,19 @@ function isControlItem(item: UnknownChangeProps | ControlItemProps): item is Con

const filterPropertyChanges = (changes: Change[], query: string): Change[] => {
return changes.filter((item): boolean => {
if (item.kind === PROPERTY_CHANGE_KIND || item.kind === CONFIGURATION_CHANGE_KIND) {
if (item.kind === GENERIC_CHANGE_KIND) {
return (
!query ||
!!item.genericProps.find(
(val) =>
String(val.label).toLowerCase().includes(query.toLowerCase()) ||
String(val.value).toLowerCase().includes(query.toLowerCase())
) ||
item.fileName.toLowerCase().includes(query.toLowerCase()) ||
(item.type === SAVED_CHANGE_TYPE &&
getFormattedDateAndTime(item.timestamp).trim().toLowerCase().includes(query))
);
} else if (item.kind === PROPERTY_CHANGE_KIND || item.kind === CONFIGURATION_CHANGE_KIND) {
return (
!query ||
item.propertyName.trim().toLowerCase().includes(query) ||
Expand Down Expand Up @@ -316,7 +398,7 @@ function filterGroup(model: Item[], query: string): Item[] {
}
for (const item of model) {
let parentMatch = false;
if (!isConfigPropGroup(item) && !isPropertyGroup(item)) {
if (!isConfigPropGroup(item) && !isPropertyGroup(item) && !isGenericChangeGroup(item)) {
if (isQueryMatchesChange(item, query)) {
filteredModel.push({ ...item, changes: [] });
}
Expand All @@ -328,7 +410,7 @@ function filterGroup(model: Item[], query: string): Item[] {
// add node without its children
filteredModel.push({ ...item, changes: [] });
}
const controlPropModel: ControlGroupProps | ConfigGroupProps = item;
const controlPropModel: ControlGroupProps | ConfigGroupProps | GenericGroupProps = item;
if (controlPropModel.changes.length <= 0) {
continue;
}
Expand All @@ -354,6 +436,20 @@ function filterGroup(model: Item[], query: string): Item[] {
* @param item ControlGroupProps | UnknownChangeProps | ConfigGroupProps
* @returns boolean
*/
function isConfigPropGroup(item: ControlGroupProps | UnknownChangeProps | ConfigGroupProps): item is ConfigGroupProps {
function isConfigPropGroup(
item: ControlGroupProps | UnknownChangeProps | ConfigGroupProps | GenericGroupProps
): item is ConfigGroupProps {
return (item as ConfigGroupProps).configPath !== undefined;
}

/**
* Checks if item is of type {@link ConfigGroupProps}.
*
* @param item ControlGroupProps | UnknownChangeProps | ConfigGroupProps
* @returns boolean
*/
function isGenericChangeGroup(
item: ControlGroupProps | UnknownChangeProps | ConfigGroupProps | GenericGroupProps
): item is GenericGroupProps {
return (item as GenericGroupProps).kind === 'generic';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
.fileLabel {
margin-top: 5px;
min-width: 30px;
line-height: 23px;
display: inline-block;
overflow: hidden;
}
.fileValue {
margin-top: 5px;
line-height: 23px;
display: inline-block;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
color: var(--vscode-editor-foreground);
width: 200px;
direction: rtl;
text-align: left;
}

.timestamp {
margin-top: 5px;
color: var(--vscode-editor-foreground);
font-size: 11px;
line-height: 15px;
opacity: 0.5;

}

.property {
overflow-wrap: anywhere;
}
.item {
padding: 1px;
padding-right: 25px;
}
.item:hover {
background-color: var(--vscode-dropdown-background);
color: var(--vscode-dropdown-foreground);
outline: 1px dashed var(--vscode-contrastActiveBorder);
.actions {
visibility: visible;
}
}

.actions {
padding-left: 10px;
margin-top: 5px;
visibility: hidden;
}
.genericValue {
display: block;
width: 100%;
max-width: 100%;
overflow-wrap: break-word;
line-height: 23px;
max-height: 46px;
}

.line {
width: 100%;
white-space: nowrap;
overflow: hidden;
}
Loading
Loading