Skip to content

Commit

Permalink
Add basic iFrame interaction
Browse files Browse the repository at this point in the history
  • Loading branch information
AmsterGet committed May 7, 2024
1 parent 60eee11 commit be75ac5
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 53 deletions.
51 changes: 7 additions & 44 deletions app/src/components/extensionLoader/extensionLoader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,58 +16,21 @@

import React from 'react';
import PropTypes from 'prop-types';
import { BubblesLoader } from '@reportportal/ui-kit';
import { ErrorBoundary } from 'components/containers/errorBoundary';
import { createImportProps } from 'controllers/plugins/uiExtensions/createImportProps';
import { ExtensionError } from './extensionError';
import { extensionType } from './extensionTypes';
import { useFederatedComponent } from './hooks';
import { getExtensionUrl } from './utils';

function FederatedExtensionLoader({ extension, withPreloader, ...componentProps }) {
const { moduleName, scope, pluginName } = extension;
const url = getExtensionUrl(extension);

const { failed, Component } = useFederatedComponent(scope, moduleName, url);

// TODO: replace with proper failed state
if (failed) {
return <h2>Failed to load extension: {moduleName}</h2>;
}

// TODO: Provide extensionImportProps via React Context
const extensionImportProps = createImportProps(pluginName);

return (
<React.Suspense fallback={withPreloader ? <BubblesLoader /> : null}>
{Component ? <Component {...extensionImportProps} {...componentProps} /> : null}
</React.Suspense>
);
}
FederatedExtensionLoader.propTypes = {
extension: extensionType,
withPreloader: PropTypes.bool,
};
FederatedExtensionLoader.defaultProps = {
extension: {},
withPreloader: false,
};

function StandaloneExtensionLoader({ extension }) {
return <iframe title={extension.pluginName} src={extension.url} />;
}
StandaloneExtensionLoader.propTypes = {
extension: extensionType,
};
StandaloneExtensionLoader.defaultProps = {
extension: {},
};
import { FederatedExtensionLoader } from './federatedExtensionLoader';
import { StandaloneExtensionLoader } from './standaloneExtensionLoader';

function ExtensionLoader({ extension, withPreloader, ...componentProps }) {
return extension.pluginType === 'remote' ? (
<StandaloneExtensionLoader extension={extension} />
) : (
<ExtensionLoader extension={extension} withPreloader={withPreloader} {...componentProps} />
<FederatedExtensionLoader
extension={extension}
withPreloader={withPreloader}
{...componentProps}
/>
);
}
ExtensionLoader.propTypes = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright 2024 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import React from 'react';
import PropTypes from 'prop-types';
import { BubblesLoader } from '@reportportal/ui-kit';
import { createImportProps } from 'controllers/plugins/uiExtensions/createImportProps';
import { getExtensionUrl } from '../utils';
import { useFederatedComponent } from '../hooks';
import { extensionType } from '../extensionTypes';

export function FederatedExtensionLoader({ extension, withPreloader, ...componentProps }) {
const { moduleName, scope, pluginName } = extension;
const url = getExtensionUrl(extension);

const { failed, Component } = useFederatedComponent(scope, moduleName, url);

// TODO: replace with proper failed state
if (failed) {
return <h2>Failed to load extension: {moduleName}</h2>;
}

// TODO: Provide extensionImportProps via React Context
const extensionImportProps = createImportProps(pluginName);

return (
<React.Suspense fallback={withPreloader ? <BubblesLoader /> : null}>
{Component ? <Component {...extensionImportProps} {...componentProps} /> : null}
</React.Suspense>
);
}
FederatedExtensionLoader.propTypes = {
extension: extensionType,
withPreloader: PropTypes.bool,
};
FederatedExtensionLoader.defaultProps = {
extension: {},
withPreloader: false,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright 2024 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export { FederatedExtensionLoader } from './federatedExtensionLoader';
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright 2024 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export { StandaloneExtensionLoader } from './standaloneExtensionLoader';
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2024 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import React, { useRef, useEffect } from 'react';
import { extensionType } from '../extensionTypes';

export function StandaloneExtensionLoader({ extension }) {
const ref = useRef();

const onLoad = () => {
// TODO: whitelist the necessary origin
ref?.current?.contentWindow.postMessage('Hello from ReportPortal', '*');
console.log('iFrame loaded, send data to it');

Check warning on line 26 in app/src/components/extensionLoader/standaloneExtensionLoader/standaloneExtensionLoader.jsx

View workflow job for this annotation

GitHub Actions / build (20)

Unexpected console statement
};

useEffect(() => {
setTimeout(() => {
onLoad();
}, 5000);
});

return (
<iframe
ref={ref}
name={extension.pluginName}
title={extension.pluginName}
src={extension.url}
style={{ width: '100%', height: '100%' }}
onLoad={onLoad}
/>
);
}
StandaloneExtensionLoader.propTypes = {
extension: extensionType,
};
StandaloneExtensionLoader.defaultProps = {
extension: {},
};
1 change: 1 addition & 0 deletions app/src/controllers/plugins/uiExtensions/sagas.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export function* fetchExtensionsMetadata(action) {

try {
const results = yield Promise.allSettled(calls);
// const results = [];
const metadataArray = results.reduce((acc, result, index) => {
if (result.status !== 'fulfilled') {
return acc;
Expand Down
3 changes: 2 additions & 1 deletion app/src/controllers/plugins/uiExtensions/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@ const createExtensionSelectorByType = (type, pluginNamesSelector = enabledPlugin
.filter(([name]) => pluginNames.includes(name))
.map(([, extensions]) => extensions);

// TODO: update
const newExtensions = extensionsMetadata
.filter(({ pluginName }) => pluginNames.includes(pluginName))
// .filter(({ pluginName }) => pluginNames.includes(pluginName))
.map(({ extensions, ...commonMetadata }) =>
extensions.map((ext) => ({ ...ext, ...commonMetadata })),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,17 @@
*/

import React from 'react';
import { BubblesLoader } from '@reportportal/ui-kit';
import { uiExtensionProjectPagesSelector } from 'controllers/plugins/uiExtensions';
import { useActivePluginPageExtension } from 'controllers/plugins/uiExtensions/hooks';
import { ExtensionLoader } from 'components/extensionLoader';
import { PageHeader, PageLayout, PageSection } from 'layouts/pageLayout';

export const ProjectUiExtensionPage = () => {
const extension = useActivePluginPageExtension(uiExtensionProjectPagesSelector);

return (
<PageLayout>
<PageHeader breadcrumbs={[{ title: extension.title || extension.name }]} />
<PageSection>
<ExtensionLoader extension={extension} silentOnError={false} withPreloader />
</PageSection>
</PageLayout>
return extension ? (
<ExtensionLoader extension={extension} silentOnError={false} withPreloader />
) : (
<BubblesLoader />
);
};
1 change: 1 addition & 0 deletions app/src/routes/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,6 @@ export const pageRendering = {
[PROJECT_PLUGIN_PAGE]: {
component: ProjectUiExtensionPage,
layout: AppLayout,
rawContent: true,
},
};

0 comments on commit be75ac5

Please sign in to comment.