Skip to content
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

Preview support for UI5 2.x #2192

Merged
merged 22 commits into from
Jul 31, 2024
Merged
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
6 changes: 6 additions & 0 deletions .changeset/preview-2.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@sap-ux-private/preview-middleware-client": patch
"@sap-ux/preview-middleware": patch
---

Preview support for UI5 2.x
3 changes: 2 additions & 1 deletion packages/preview-middleware-client/src/adp/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ import VersionInfo from 'sap/ui/VersionInfo';
import { getUI5VersionValidationMessage } from './ui5-version-utils';
import UI5Element from 'sap/ui/dt/Element';
import { getError } from '../cpe/error-utils';
import type {SingleVersionInfo} from '../../types/global';

export default async function (rta: RuntimeAuthoring) {
const { version } = (await VersionInfo.load()) as { version: string };
const version = (await VersionInfo.load({library:'sap.ui.core'}) as SingleVersionInfo)?.version;
const versionParts = version.split('.');
const minor = parseInt(versionParts[1], 10);
const flexSettings = rta.getFlexSettings();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ExternalAction, storageFileChanged } from '@sap-ux-private/control-property-editor-common';
import { ActionSenderFunction } from './types';
import VersionInfo from 'sap/ui/VersionInfo';
import type {SingleVersionInfo} from '../../types/global';

/**
* A Class of WorkspaceConnectorService
Expand All @@ -12,7 +13,7 @@ export class WorkspaceConnectorService {
* @param sendAction action sender function
*/
public async init(sendAction: ActionSenderFunction): Promise<void> {
const { version } = (await VersionInfo.load()) as { version: string };
const version = (await VersionInfo.load({library:'sap.ui.core'}) as SingleVersionInfo)?.version;
const versionParts = version.split('.');
const minor = parseInt(versionParts[1], 10);
if (minor > 72) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { OutlineViewNode } from 'sap/ui/rta/command/OutlineService';
import type { Scenario } from 'sap/ui/fl/Scenario';
import { isEditable, isReuseComponent } from './utils';
import VersionInfo from 'sap/ui/VersionInfo';
import type {SingleVersionInfo} from '../../../types/global';

interface AdditionalData {
text?: string;
Expand Down Expand Up @@ -87,7 +88,7 @@ export async function transformNodes(
): Promise<OutlineNode[]> {
const stack = [...input];
const items: OutlineNode[] = [];
const { version } = (await VersionInfo.load()) as { version: string };
const version = (await VersionInfo.load({library:'sap.ui.core'}) as SingleVersionInfo)?.version;
const versionParts = version.split('.');
const minor = parseInt(versionParts[1], 10);
while (stack.length) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import merge from 'sap/base/util/merge';
import ObjectStorageConnector from 'sap/ui/fl/write/api/connectors/ObjectStorageConnector';
import Layer from 'sap/ui/fl/Layer';
import VersionInfo from 'sap/ui/VersionInfo';
import type {SingleVersionInfo} from '../../types/global';
import { CHANGES_API_PATH, FlexChange, getFlexSettings } from './common';

const connector = merge({}, ObjectStorageConnector, {
Expand Down Expand Up @@ -69,8 +70,8 @@ const connector = merge({}, ObjectStorageConnector, {
loadFeatures: async function () {
const features = await ObjectStorageConnector.loadFeatures();

const ui5Version = (await VersionInfo.load()) as { version: string };
const [majorVersion, minorVersion] = ui5Version.version.split('.').map((v: string) => parseInt(v, 10));
const version = (await VersionInfo.load({library:'sap.ui.core'}) as SingleVersionInfo)?.version;
const [majorVersion, minorVersion] = version.split('.').map((v: string) => parseInt(v, 10));
features.isVariantAdaptationEnabled = majorVersion >= 1 && minorVersion >= 90;
const settings = getFlexSettings();
if (settings?.developerMode) {
Expand Down
38 changes: 38 additions & 0 deletions packages/preview-middleware-client/src/flp/bootstrap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment,
@typescript-eslint/no-unsafe-member-access,
@typescript-eslint/no-unsafe-assignment,
@typescript-eslint/no-unsafe-argument,
no-console */

/**
* Calculates the script content for accessing the right sap/ushell/bootstrap sandbox.
* @param fnCallback {Function} The callback function to be executed after the bootstrap is loaded.
*/
async function ushellBootstrap(fnCallback) {
let src = '/test-resources/sap/ushell/bootstrap/sandbox.js';
try {
const response = await fetch('/resources/sap-ui-version.json');
const json = await response.json();
const version = json?.libraries?.find((lib) => lib.name === 'sap.ui.core')?.version;
const major = version ? parseInt(version.split('.')[0], 10) : 2;
if (major >= 2) {
src = '/resources/sap/ushell/bootstrap/sandbox2.js';
}
} catch (error) {
console.warn('Failed to fetch sap-ui-version.json. Assuming it is a 1.x version.');
}

// eslint-disable-next-line fiori-custom/sap-no-dom-access,fiori-custom/sap-browser-api-warning
const shellBootstrap = document.getElementById('sap-ushell-bootstrap');
if (shellBootstrap) {
shellBootstrap.onload = () => {
window['sap-ui-config']['xx-bootTask'](fnCallback);
};
shellBootstrap.setAttribute('src', src);
}
}

// eslint-disable-next-line fiori-custom/sap-no-global-define
window['sap-ui-config'] = {
'xx-bootTask': ushellBootstrap
};
24 changes: 19 additions & 5 deletions packages/preview-middleware-client/src/flp/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import IconPool from 'sap/ui/core/IconPool';
import ResourceBundle from 'sap/base/i18n/ResourceBundle';
import AppState from 'sap/ushell/services/AppState';
import { getManifestAppdescr } from '../adp/api-handler';
import VersionInfo from 'sap/ui/VersionInfo';
import { getError } from '../cpe/error-utils';
import initConnectors from './initConnectors';
tobiasqueck marked this conversation as resolved.
Show resolved Hide resolved
import type {SingleVersionInfo} from '../../types/global';

/**
* SAPUI5 delivered namespaces from https://ui5.sap.com/#/api/sap
Expand Down Expand Up @@ -264,16 +268,16 @@ export async function init({
customInit?: string | null;
}): Promise<void> {
const urlParams = new URLSearchParams(window.location.search);
const container = sap?.ushell?.Container ?? (sap.ui.require('sap/ushell/Container') as typeof sap.ushell.Container);
const container = sap?.ushell?.Container ?? sap.ui.require('sap/ushell/Container');
let scenario: string = '';
const version = (await VersionInfo.load({library:'sap.ui.core'}) as SingleVersionInfo)?.version;
// Register RTA if configured
if (flex) {
const flexSettings = JSON.parse(flex) as FlexSettings;
scenario = flexSettings.scenario;
container.attachRendererCreatedEvent(async function () {
const lifecycleService = await container.getServiceAsync<AppLifeCycle>('AppLifeCycle');
lifecycleService.attachAppLoaded((event) => {
const version = sap.ui.version;
const minor = parseInt(version.split('.')[1], 10);
const view = event.getParameter('componentInstance');
const flexSettings = JSON.parse(flex) as FlexSettings;
Expand Down Expand Up @@ -317,6 +321,9 @@ export async function init({
await registerComponentDependencyPaths(JSON.parse(appUrls), urlParams);
}

// Load rta connector
Klaus-Keller marked this conversation as resolved.
Show resolved Hide resolved
await initConnectors();

// Load custom initialization module
if (customInit) {
sap.ui.require([customInit]);
Expand All @@ -326,15 +333,22 @@ export async function init({
const resourceBundle = await loadI18nResourceBundle(scenario as Scenario);
setI18nTitle(resourceBundle);
registerSAPFonts();
const renderer = await container.createRenderer(undefined, true);
const major = version ? parseInt(version.split('.')[0], 10) : 2;

const renderer =
major < 2
? await container.createRenderer(undefined, true)
: await container.createRendererInternal(undefined, true);
renderer.placeAt('content');
}

const bootstrapConfig = document.getElementById('sap-ui-bootstrap');
if (bootstrapConfig) {
init({
appUrls: bootstrapConfig.getAttribute('data-open-ux-preview-libs-manifests'),
flex: bootstrapConfig.getAttribute('data-open-ux-preview-flex-settings'),
customInit: bootstrapConfig.getAttribute('data-open-ux-preview-customInit')
}).catch(() => Log.error('Sandbox initialization failed.'));
}).catch((e) => {
const error = getError(e);
Log.error('Sandbox initialization failed: ' + error.message);
});
}
16 changes: 9 additions & 7 deletions packages/preview-middleware-client/src/flp/initConnectors.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import VersionInfo from 'sap/ui/VersionInfo';
import type {SingleVersionInfo} from '../../types/global';

/**
* Initializes UI5 connectors based on the current UI5 version.
*
Expand All @@ -7,13 +10,14 @@
*
* @example
* intiConnectors(); // Simply call the function without any arguments.
* @returns {void}
*/
export function initConnectors(): void {
const version = sap.ui.version;
const minor = parseInt(version.split('.')[1], 10);
export default async function initConnectors(): Promise<void> {
const version = (await VersionInfo.load({library:'sap.ui.core'}) as SingleVersionInfo)?.version;
const versionArray = version ? version.split('.') : ['2', '99'];
const minor = parseInt(versionArray[1], 10);
const major = parseInt(versionArray[0], 10);

if (minor < 72) {
if (major === 1 && minor < 72) {
sap.ui.require(['open/ux/preview/client/flp/enableFakeConnector'], function (enableFakeConnector: () => void) {
enableFakeConnector();
});
Expand All @@ -26,5 +30,3 @@ export function initConnectors(): void {
);
}
}

initConnectors();
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const sapMock = {
ushell: {
Container: {
createRenderer: jest.fn().mockReturnValue({ placeAt: jest.fn() }),
createRendererInternal: jest.fn().mockReturnValue({ placeAt: jest.fn() }),
attachRendererCreatedEvent: jest.fn(),
getServiceAsync: jest.fn()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ describe('adp', () => {
return { dispose: jest.fn(), sendAction: jest.fn() };
});
const enableTelemetry = jest.spyOn(common, 'enableTelemetry');
VersionInfo.load.mockResolvedValue({ version: '1.118.1' });
VersionInfo.load.mockResolvedValue({name: 'sap.ui.core', version: '1.118.1' });

await init(rtaMock as unknown as RuntimeAuthoring);

Expand Down Expand Up @@ -93,7 +93,7 @@ describe('adp', () => {
return { dispose: jest.fn(), sendAction: sendActionMock };
});

VersionInfo.load.mockResolvedValue({ version: '1.70.0' });
VersionInfo.load.mockResolvedValue({name: 'sap.ui.core', version: '1.70.0' });

await init(rtaMock as unknown as RuntimeAuthoring);

Expand Down Expand Up @@ -133,7 +133,7 @@ describe('adp', () => {
'application-app-preview-component---fin.ar.lineitems.display.appView': mockUI5Element
});

VersionInfo.load.mockResolvedValue({ version: '1.123.1' });
VersionInfo.load.mockResolvedValue({name: 'sap.ui.core', version: '1.123.1' });

await init(rtaMock as unknown as RuntimeAuthoring);

Expand Down Expand Up @@ -172,7 +172,7 @@ describe('adp', () => {

Element.registry.filter.mockReturnValue([mockUI5Element]);

VersionInfo.load.mockResolvedValue({ version: '1.118.1' });
VersionInfo.load.mockResolvedValue({name: 'sap.ui.core', version: '1.118.1' });

await init(rtaMock as unknown as RuntimeAuthoring);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('connector-service', () => {
fetchMock.mockRestore();
});
test('init - ui5 > v1.72', async () => {
VersionInfo.load.mockResolvedValue({ version: '1.120.4' });
VersionInfo.load.mockResolvedValue({ name: 'sap.ui.core', version: '1.120.4' });
const wsConnector = new WorkspaceConnectorService();
await wsConnector.init(sendActionMock);

Expand All @@ -25,7 +25,7 @@ describe('connector-service', () => {
});

test('init - ui5 < v1.72', async () => {
VersionInfo.load.mockResolvedValue({ version: '1.71.67' });
VersionInfo.load.mockResolvedValue({name: 'sap.ui.core', version: '1.71.67' });
const wsConnector = new WorkspaceConnectorService();
await wsConnector.init(sendActionMock);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe('outline nodes', () => {
});

beforeAll(() => {
VersionInfo.load.mockResolvedValue({ version: '1.118.1' });
VersionInfo.load.mockResolvedValue({name: 'sap.ui.core', version: '1.118.1' });
})

describe('transformNodes', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,19 +97,19 @@ describe('flp/WorkspaceConnector', () => {
});

test('version >= 1.90, no developerMode', async () => {
VersionInfo.load.mockResolvedValueOnce({ version: '1.118.1' });
VersionInfo.load.mockResolvedValueOnce({name: 'sap.ui.core', version: '1.118.1' });
const features = await connector.loadFeatures();
expect(features.isVariantAdaptationEnabled).toBe(true);
});

test('version < 1.90', async () => {
VersionInfo.load.mockResolvedValueOnce({ version: '1.89.3' });
VersionInfo.load.mockResolvedValueOnce({name: 'sap.ui.core', version: '1.89.3' });
const features = await connector.loadFeatures();
expect(features.isVariantAdaptationEnabled).toBe(false);
});

test('version >= 1.90, developerMode=true', async () => {
VersionInfo.load.mockResolvedValueOnce({ version: '1.118.1' });
VersionInfo.load.mockResolvedValueOnce({name: 'sap.ui.core', version: '1.118.1' });
documentMock.getElementById.mockReturnValueOnce({
getAttribute: () => JSON.stringify({ developerMode: true })
});
Expand All @@ -118,7 +118,7 @@ describe('flp/WorkspaceConnector', () => {
});

test('scenario=ADAPTATION_PROJECT', async () => {
VersionInfo.load.mockResolvedValueOnce({ version: '1.118.1' });
VersionInfo.load.mockResolvedValueOnce({name: 'sap.ui.core', version: '1.118.1' });
documentMock.getElementById.mockReturnValueOnce({
getAttribute: () => JSON.stringify({ scenario: 'ADAPTATION_PROJECT' })
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { documentMock } from 'mock/window';
import '../../../src/flp/bootstrap';
import { Window } from '../../../types/global';

describe('flp/ushellBootstrap', () => {
const htmlElement = {
onload: jest.fn(),
setAttribute: jest.fn()
};
documentMock.getElementById.mockReturnValue(htmlElement);
const fetchMock = jest.spyOn(global, 'fetch');

const ushellBootstrap = (window as unknown as Window)['sap-ui-config']['xx-bootTask'];

afterEach(() => {
jest.clearAllMocks();
});

test('xx-boottask defined', () => {
expect(ushellBootstrap).toBeDefined();
});

test('ushell src when ui5 version is 1.x', async () => {
fetchMock.mockResolvedValueOnce({
json: () => Promise.resolve({ libraries: [{name: 'sap.ui.core', version: '1.126.0'}] })
} as jest.Mocked<Response>);

await ushellBootstrap(() => {});
expect(htmlElement.setAttribute).toHaveBeenCalledWith('src', '/test-resources/sap/ushell/bootstrap/sandbox.js');
});

test('ushell src when ui5 version is 2.0', async () => {
fetchMock.mockResolvedValue({
json: () => Promise.resolve({ libraries: [{name: 'sap.ui.core', version: '2.0.0'}] })
} as jest.Mocked<Response>);

await ushellBootstrap(() => {});
expect(htmlElement.setAttribute).toHaveBeenCalledWith('src', '/resources/sap/ushell/bootstrap/sandbox2.js');
});

test('fetching version failed', async () => {
fetchMock.mockRejectedValueOnce('404');

await ushellBootstrap(() => {});
expect(htmlElement.setAttribute).toHaveBeenCalledWith('src', '/test-resources/sap/ushell/bootstrap/sandbox.js');
});
});
Loading
Loading