From 61bee669d17614e1ea57ea5d5bff5651f982c664 Mon Sep 17 00:00:00 2001 From: Davis Date: Fri, 20 Sep 2024 10:29:19 +0300 Subject: [PATCH] fix: quick action improvements --- .../control-property-editor-common/src/api.ts | 6 +- packages/control-property-editor/src/slice.ts | 12 ++-- .../src/toolbar/UndoRedoSaveActions.tsx | 6 +- .../test/unit/slice.test.ts | 12 ++-- .../unit/toolbar/UndoRedoSaveActions.test.tsx | 4 +- .../adp/controllers/AddFragment.controller.ts | 26 +++++-- .../src/adp/extension-point.ts | 7 +- .../src/adp/init-dialogs.ts | 18 ++--- .../preview-middleware-client/src/adp/init.ts | 38 ++-------- .../common/op-add-custom-section.ts | 5 +- .../common/op-add-header-field.ts | 10 ++- .../src/adp/ui/AddFragment.fragment.xml | 13 ++-- .../src/cpe/changes/service.ts | 8 +-- .../src/cpe/communication-service.ts | 41 +++++++++++ .../src/cpe/connector-service.ts | 15 +++- .../preview-middleware-client/src/cpe/init.ts | 28 ++------ .../src/flp/WorkspaceConnector.ts | 2 +- .../src/flp/enableFakeConnector.ts | 2 +- .../preview-middleware-client/src/i18n.ts | 15 ++++ .../src/messagebundle.properties | 5 ++ .../sap/ui/model/resource/ResourceModel.ts | 5 ++ .../AddFragment.controller.test.ts | 70 +++++++++++++++---- .../test/unit/adp/init.test.ts | 39 ++++------- .../test/unit/adp/quick-actions/fe-v2.test.ts | 10 ++- .../test/unit/adp/quick-actions/fe-v4.test.ts | 5 +- .../test/unit/cpe/changes/service.test.ts | 4 +- .../test/unit/cpe/connector-service.test.ts | 39 +++++++++++ .../test/unit/cpe/init.test.ts | 13 ++-- .../test/unit/flp/WorkspaceConnector.test.ts | 2 +- .../types/sap.ui.fl.d.ts | 2 +- .../types/sap.ui.rta.d.ts | 2 +- 31 files changed, 303 insertions(+), 161 deletions(-) create mode 100644 packages/preview-middleware-client/src/cpe/communication-service.ts create mode 100644 packages/preview-middleware-client/test/__mock__/sap/ui/model/resource/ResourceModel.ts diff --git a/packages/control-property-editor-common/src/api.ts b/packages/control-property-editor-common/src/api.ts index 7ab7b20c7d..bbcdd61701 100644 --- a/packages/control-property-editor-common/src/api.ts +++ b/packages/control-property-editor-common/src/api.ts @@ -299,9 +299,7 @@ export const save = createExternalAction('save'); export const quickActionListChanged = createExternalAction('quick-action-list-changed'); export const updateQuickAction = createExternalAction('update-quick-action'); export const executeQuickAction = createExternalAction('execute-quick-action'); -export const numberOfChangesRequiringReloadChanged = createExternalAction( - 'number-of-changes-requiring-reload-changed' -); +export const setApplicationRequiresReload = createExternalAction('set-application-requires-reload'); export type ExternalAction = | ReturnType @@ -325,6 +323,6 @@ export type ExternalAction = | ReturnType | ReturnType | ReturnType - | ReturnType + | ReturnType | ReturnType | ReturnType; diff --git a/packages/control-property-editor/src/slice.ts b/packages/control-property-editor/src/slice.ts index 30eb64577d..9d0c828edc 100644 --- a/packages/control-property-editor/src/slice.ts +++ b/packages/control-property-editor/src/slice.ts @@ -15,7 +15,7 @@ import type { ShowMessage } from '@sap-ux-private/control-property-editor-common'; import { - numberOfChangesRequiringReloadChanged, + setApplicationRequiresReload, changeStackModified, controlSelected, iconsLoaded, @@ -61,7 +61,7 @@ interface SliceState { canRedo: boolean; }; canSave: boolean; - pendingChangesRequiresSaveAndReload: boolean; + applicationRequiresReload: boolean; isAppLoading: boolean; quickActions: QuickActionGroup[]; } @@ -149,7 +149,7 @@ export const initialState: SliceState = { canRedo: false }, canSave: false, - pendingChangesRequiresSaveAndReload: false, + applicationRequiresReload: false, isAppLoading: true, quickActions: [] }; @@ -324,9 +324,9 @@ const slice = createSlice, string>({ state.isAppLoading = false; }) .addMatcher( - numberOfChangesRequiringReloadChanged.match, - (state, action: ReturnType): void => { - state.pendingChangesRequiresSaveAndReload = action.payload > 0; + setApplicationRequiresReload.match, + (state, action: ReturnType): void => { + state.applicationRequiresReload = action.payload; } ) .addMatcher( diff --git a/packages/control-property-editor/src/toolbar/UndoRedoSaveActions.tsx b/packages/control-property-editor/src/toolbar/UndoRedoSaveActions.tsx index ab92b6e21d..b07d65cfcc 100644 --- a/packages/control-property-editor/src/toolbar/UndoRedoSaveActions.tsx +++ b/packages/control-property-editor/src/toolbar/UndoRedoSaveActions.tsx @@ -23,11 +23,9 @@ export function UndoRedoSaveActions(): ReactElement { const canSave = useSelector((state) => state.canSave); const isLoading = useSelector((state) => state.isAppLoading); const fileChanges = useSelector((state) => state.fileChanges) ?? []; - const pendingChangesRequiresSaveAndReload = useSelector( - (state) => state.pendingChangesRequiresSaveAndReload - ); + const applicationRequiresReload = useSelector((state) => state.applicationRequiresReload); const { pending } = useSelector((state) => state.changes); - const saveAndReload = (fileChanges.length > 0 && pending.length > 0) || pendingChangesRequiresSaveAndReload; + const saveAndReload = (fileChanges.length > 0 && pending.length > 0) || applicationRequiresReload; return ( <> { deviceType: DeviceType.Desktop }); }); - describe('numberOfChangesRequiringReloadChanged', () => { + describe('setApplicationRequiresReload', () => { test('one change requires reload', () => { expect( - reducer({ pendingChangesRequiresSaveAndReload: false } as any, numberOfChangesRequiringReloadChanged(1)) + reducer({ applicationRequiresReload: false } as any, setApplicationRequiresReload(true)) ).toStrictEqual({ - pendingChangesRequiresSaveAndReload: true + applicationRequiresReload: true }); }); test('no changes require reload', () => { expect( - reducer({ pendingChangesRequiresSaveAndReload: true } as any, numberOfChangesRequiringReloadChanged(0)) + reducer({ setApplicationRequiresReload: true } as any, setApplicationRequiresReload(false)) ).toStrictEqual({ - pendingChangesRequiresSaveAndReload: false + applicationRequiresReload: false }); }); }); diff --git a/packages/control-property-editor/test/unit/toolbar/UndoRedoSaveActions.test.tsx b/packages/control-property-editor/test/unit/toolbar/UndoRedoSaveActions.test.tsx index 6b90b1b52d..fe2bc12df9 100644 --- a/packages/control-property-editor/test/unit/toolbar/UndoRedoSaveActions.test.tsx +++ b/packages/control-property-editor/test/unit/toolbar/UndoRedoSaveActions.test.tsx @@ -11,7 +11,7 @@ import { redo, save, undo, - numberOfChangesRequiringReloadChanged, + setApplicationRequiresReload, reloadApplication } from '@sap-ux-private/control-property-editor-common'; @@ -54,7 +54,7 @@ describe('toolbar', () => { // update state store.dispatch(setUndoRedoEnablement({ canRedo: true, canUndo: true })); store.dispatch(setSaveEnablement(true)); - store.dispatch(numberOfChangesRequiringReloadChanged(5)); + store.dispatch(setApplicationRequiresReload(true)); store.dispatch(appLoaded()); dispatch.mockClear(); diff --git a/packages/preview-middleware-client/src/adp/controllers/AddFragment.controller.ts b/packages/preview-middleware-client/src/adp/controllers/AddFragment.controller.ts index 941c5c080e..4f1885534b 100644 --- a/packages/preview-middleware-client/src/adp/controllers/AddFragment.controller.ts +++ b/packages/preview-middleware-client/src/adp/controllers/AddFragment.controller.ts @@ -21,7 +21,12 @@ import OverlayRegistry from 'sap/ui/dt/OverlayRegistry'; import type ElementOverlay from 'sap/ui/dt/ElementOverlay'; /** sap.ui.fl */ -import {type AddFragmentChangeContentType} from 'sap/ui/fl/Change'; +import { type AddFragmentChangeContentType } from 'sap/ui/fl/Change'; + +import { setApplicationRequiresReload } from '@sap-ux-private/control-property-editor-common'; + +import { getResourceModel } from '../../i18n'; +import { CommunicationService } from '../../cpe/communication-service'; import ControlUtils from '../control-utils'; import CommandExecutor from '../command-executor'; @@ -38,20 +43,30 @@ interface CreateFragmentProps { const radix = 10; type AddFragmentModel = JSONModel & { + getProperty(sPath: '/title'): string; + getProperty(sPath: '/completeView'): boolean; getProperty(sPath: '/newFragmentName'): string; getProperty(sPath: '/selectedIndex'): number; getProperty(sPath: '/selectedAggregation/value'): string; }; +export interface AddFragmentOptions { + title: string; + aggregation?: string; +} + /** * @namespace open.ux.preview.client.adp.controllers */ export default class AddFragment extends BaseDialog { - constructor(name: string, overlays: UI5Element, rta: RuntimeAuthoring, private aggregation?: string) { + constructor(name: string, overlays: UI5Element, rta: RuntimeAuthoring, private options: AddFragmentOptions) { super(name); this.rta = rta; this.overlays = overlays; - this.model = new JSONModel(); + this.model = new JSONModel({ + title: options.title, + completeView: options.aggregation === undefined + }); this.ui5Version = sap.ui.version; this.commandExecutor = new CommandExecutor(this.rta); } @@ -67,7 +82,9 @@ export default class AddFragment extends BaseDialog { this.setEscapeHandler(); await this.buildDialogData(); + const resourceModel = await getResourceModel('open.ux.preview.client'); + this.dialog.setModel(resourceModel, 'i18n'); this.dialog.setModel(this.model); this.dialog.open(); @@ -184,7 +201,7 @@ export default class AddFragment extends BaseDialog { } return false; }); - const defaultAggregation = this.aggregation ?? controlMetadata.getDefaultAggregationName(); + const defaultAggregation = this.options.aggregation ?? controlMetadata.getDefaultAggregationName(); const selectedControlName = controlMetadata.getName(); let selectedControlChildren: string[] | number[] = Object.keys( @@ -290,6 +307,7 @@ export default class AddFragment extends BaseDialog { const preparedChange = command.getPreparedChange(); const content = preparedChange.getContent(); preparedChange.setContent({ ...content, templateName }); + CommunicationService.sendAction(setApplicationRequiresReload(true)); } await this.commandExecutor.pushAndExecuteCommand(command); } diff --git a/packages/preview-middleware-client/src/adp/extension-point.ts b/packages/preview-middleware-client/src/adp/extension-point.ts index 4d216f11c1..8a45a91d7f 100644 --- a/packages/preview-middleware-client/src/adp/extension-point.ts +++ b/packages/preview-middleware-client/src/adp/extension-point.ts @@ -8,7 +8,7 @@ import { ExternalAction, addExtensionPoint } from '@sap-ux-private/control-prope import { Deferred, createDeferred } from './utils'; -import { SubscribeFunction } from '../cpe/types'; +import { CommunicationService } from '../cpe/communication-service'; import { DialogNames, handler } from './init-dialogs'; type ActionService = { @@ -50,11 +50,10 @@ export default class ExtensionPointService { /** * Initializes communication with CPE, and the extension point plugin. * - * @param subscribe Handles actions from CPE */ - public init(subscribe: SubscribeFunction) { + public init() { this.initPlugin(); - subscribe(async (action: ExternalAction): Promise => { + CommunicationService.subscribe(async (action: ExternalAction): Promise => { if (addExtensionPoint.match(action)) { try { const { controlId, name } = action.payload; diff --git a/packages/preview-middleware-client/src/adp/init-dialogs.ts b/packages/preview-middleware-client/src/adp/init-dialogs.ts index a1ff345ed8..8b649c27df 100644 --- a/packages/preview-middleware-client/src/adp/init-dialogs.ts +++ b/packages/preview-middleware-client/src/adp/init-dialogs.ts @@ -14,13 +14,14 @@ import FlUtils from 'sap/ui/fl/Utils'; /** sap.ui.dt */ import type ElementOverlay from 'sap/ui/dt/ElementOverlay'; -import AddFragment from './controllers/AddFragment.controller'; +import AddFragment, { AddFragmentOptions } from './controllers/AddFragment.controller'; import ControllerExtension from './controllers/ControllerExtension.controller'; import { ExtensionPointData } from './extension-point'; import ExtensionPoint from './controllers/ExtensionPoint.controller'; import ManagedObject from 'sap/ui/base/ManagedObject'; import { isReuseComponent } from '../cpe/utils'; import { Ui5VersionInfo } from '../utils/version'; +import { getTextBundle } from '../i18n'; export const enum DialogNames { ADD_FRAGMENT = 'AddFragment', @@ -118,25 +119,24 @@ export const getAddFragmentItemText = (overlay: ElementOverlay) => { * @param rta Runtime Authoring * @param dialogName Dialog name * @param extensionPointData Control ID - * @param aggregation Name of aggregation that should be selected when dialog is opened + * @param options Dialog options */ export async function handler( overlay: UI5Element, rta: RuntimeAuthoring, dialogName: DialogNames, extensionPointData?: ExtensionPointData, - aggregation?: string + options: Partial = {} ): Promise { let controller: Controller; + const resources = await getTextBundle(); switch (dialogName) { case DialogNames.ADD_FRAGMENT: - controller = new AddFragment( - `open.ux.preview.client.adp.controllers.${dialogName}`, - overlay, - rta, - aggregation - ); + controller = new AddFragment(`open.ux.preview.client.adp.controllers.${dialogName}`, overlay, rta, { + aggregation: options.aggregation, + title: resources.getText(options.title ?? 'ADP_ADD_FRAGMENT_DIALOG_TITLE') + }); break; case DialogNames.CONTROLLER_EXTENSION: controller = new ControllerExtension(`open.ux.preview.client.adp.controllers.${dialogName}`, overlay, rta); diff --git a/packages/preview-middleware-client/src/adp/init.ts b/packages/preview-middleware-client/src/adp/init.ts index 722119ed9a..2a3478de00 100644 --- a/packages/preview-middleware-client/src/adp/init.ts +++ b/packages/preview-middleware-client/src/adp/init.ts @@ -1,17 +1,11 @@ import log from 'sap/base/Log'; import type RuntimeAuthoring from 'sap/ui/rta/RuntimeAuthoring'; -import { - ExternalAction, - showMessage, - startPostMessageCommunication, - enableTelemetry -} from '@sap-ux-private/control-property-editor-common'; +import { showMessage, enableTelemetry } from '@sap-ux-private/control-property-editor-common'; -import { ActionHandler } from '../cpe/types'; -import { getError } from '../utils/error'; import { getUi5Version, getUI5VersionValidationMessage, isLowerThanMinimalUi5Version } from '../utils/version'; +import { CommunicationService } from '../cpe/communication-service'; import init from '../cpe/init'; import { getApplicationType } from '../utils/application'; import { getTextBundle } from '../i18n'; @@ -25,27 +19,7 @@ export default async function (rta: RuntimeAuthoring) { if (flexSettings.telemetry === true) { enableTelemetry(); } - const actionHandlers: ActionHandler[] = []; - /** - * - * @param handler action handler - */ - function subscribe(handler: ActionHandler): void { - actionHandlers.push(handler); - } - const { sendAction } = startPostMessageCommunication( - window.parent, - async function onAction(action: ExternalAction) { - for (const handler of actionHandlers) { - try { - await handler(action); - } catch (error) { - log.error('Handler Failed: ', getError(error)); - } - } - } - ); const ui5VersionInfo = await getUi5Version(); const syncViewsIds = await getAllSyncViewsIds(ui5VersionInfo); @@ -54,7 +28,7 @@ export default async function (rta: RuntimeAuthoring) { if (!isLowerThanMinimalUi5Version(ui5VersionInfo, { major: 1, minor: 78 })) { const ExtensionPointService = (await import('open/ux/preview/client/adp/extension-point')).default; const extPointService = new ExtensionPointService(rta); - extPointService.init(subscribe); + extPointService.init(); } const applicationType = getApplicationType(rta.getRootControlInstance().getManifest()); @@ -63,13 +37,15 @@ export default async function (rta: RuntimeAuthoring) { await init(rta, quickActionRegistries); if (isLowerThanMinimalUi5Version(ui5VersionInfo)) { - sendAction(showMessage({ message: getUI5VersionValidationMessage(ui5VersionInfo), shouldHideIframe: true })); + CommunicationService.sendAction( + showMessage({ message: getUI5VersionValidationMessage(ui5VersionInfo), shouldHideIframe: true }) + ); return; } if (syncViewsIds.length > 0) { const bundle = await getTextBundle(); - sendAction( + CommunicationService.sendAction( showMessage({ message: bundle.getText('ADP_SYNC_VIEWS_MESSAGE'), shouldHideIframe: false diff --git a/packages/preview-middleware-client/src/adp/quick-actions/common/op-add-custom-section.ts b/packages/preview-middleware-client/src/adp/quick-actions/common/op-add-custom-section.ts index 1f7f81e18d..4764c7e598 100644 --- a/packages/preview-middleware-client/src/adp/quick-actions/common/op-add-custom-section.ts +++ b/packages/preview-middleware-client/src/adp/quick-actions/common/op-add-custom-section.ts @@ -29,7 +29,10 @@ export class AddCustomSectionQuickAction )[0] as ObjectPageLayout; const overlay = OverlayRegistry.getOverlay(objectPageLayout) || []; - await handler(overlay, this.context.rta, DialogNames.ADD_FRAGMENT, undefined, 'sections'); + await handler(overlay, this.context.rta, DialogNames.ADD_FRAGMENT, undefined, { + aggregation: 'sections', + title: 'QUICK_ACTION_OP_ADD_CUSTOM_SECTION' + }); return []; } } diff --git a/packages/preview-middleware-client/src/adp/quick-actions/common/op-add-header-field.ts b/packages/preview-middleware-client/src/adp/quick-actions/common/op-add-header-field.ts index 2e252989cb..7059f38738 100644 --- a/packages/preview-middleware-client/src/adp/quick-actions/common/op-add-header-field.ts +++ b/packages/preview-middleware-client/src/adp/quick-actions/common/op-add-header-field.ts @@ -32,10 +32,16 @@ export class AddHeaderFieldQuickAction extends SimpleQuickActionDefinitionBase i // check if only flex box exist in the headerContent. if (headerContent.length === 1 && isA('sap.m.FlexBox', headerContent[0])) { const overlay = OverlayRegistry.getOverlay(headerContent[0]) || []; - await handler(overlay, this.context.rta, DialogNames.ADD_FRAGMENT, undefined, 'items'); + await handler(overlay, this.context.rta, DialogNames.ADD_FRAGMENT, undefined, { + aggregation: 'items', + title: 'QUICK_ACTION_OP_ADD_HEADER_FIELD' + }); } else if (this.control) { const overlay = OverlayRegistry.getOverlay(this.control) || []; - await handler(overlay, this.context.rta, DialogNames.ADD_FRAGMENT, undefined, 'headerContent'); + await handler(overlay, this.context.rta, DialogNames.ADD_FRAGMENT, undefined, { + aggregation: 'headerContent', + title: 'QUICK_ACTION_OP_ADD_HEADER_FIELD' + }); } return []; } diff --git a/packages/preview-middleware-client/src/adp/ui/AddFragment.fragment.xml b/packages/preview-middleware-client/src/adp/ui/AddFragment.fragment.xml index df508b867a..2c8f137b54 100644 --- a/packages/preview-middleware-client/src/adp/ui/AddFragment.fragment.xml +++ b/packages/preview-middleware-client/src/adp/ui/AddFragment.fragment.xml @@ -2,7 +2,7 @@ xmlns="sap.m" xmlns:core="sap.ui.core" xmlns:f="sap.ui.layout.form" - title="Add XML Fragment" + title="{/title}" contentWidth="500px" class="sapUiRTABorder"> @@ -12,10 +12,11 @@ labelSpanS="4" singleContainerFullSize="false"> -