refactor(frontend-plugin-api): merge boundary and suspense components
Signed-off-by: Camila Belo <camilaibs@gmail.com>
This commit is contained in:
@@ -340,7 +340,7 @@ export interface ExtensionBoundaryProps {
|
||||
// (undocumented)
|
||||
id: string;
|
||||
// (undocumented)
|
||||
routeRef?: RouteRef;
|
||||
routable?: boolean;
|
||||
// (undocumented)
|
||||
source?: BackstagePlugin;
|
||||
}
|
||||
@@ -416,17 +416,6 @@ export interface ExtensionOverridesOptions {
|
||||
extensions: Extension<unknown>[];
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export function ExtensionSuspense(
|
||||
props: ExtensionSuspenseProps,
|
||||
): React_2.JSX.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ExtensionSuspenseProps {
|
||||
// (undocumented)
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface ExternalRouteRef<
|
||||
TParams extends AnyRouteRefParams = AnyRouteRefParams,
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
*/
|
||||
|
||||
import React, { Component, PropsWithChildren } from 'react';
|
||||
// TODO: Dependency on MUI should be removed from core packages
|
||||
import { Button } from '@material-ui/core';
|
||||
import { BackstagePlugin } from '../wiring';
|
||||
import { ErrorPanel } from '@backstage/core-components';
|
||||
import { BackstagePlugin } from '../wiring';
|
||||
|
||||
type DefaultErrorBoundaryFallbackProps = PropsWithChildren<{
|
||||
plugin?: BackstagePlugin;
|
||||
|
||||
@@ -33,7 +33,6 @@ import { analyticsApiRef, useAnalytics } from '@backstage/core-plugin-api';
|
||||
import { createApp } from '@backstage/frontend-app-api';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { createRouteRef } from '../routing';
|
||||
import { toInternalRouteRef } from '../routing/RouteRef';
|
||||
|
||||
function renderExtensionInTestApp(
|
||||
extension: Extension<unknown>,
|
||||
@@ -59,7 +58,6 @@ function renderExtensionInTestApp(
|
||||
const wrapInBoundaryExtension = (element: JSX.Element) => {
|
||||
const id = 'plugin.extension';
|
||||
const routeRef = createRouteRef();
|
||||
toInternalRouteRef(routeRef).setId(id);
|
||||
return createExtension({
|
||||
id,
|
||||
attachTo: { id: 'core.routes', input: 'routes' },
|
||||
@@ -73,7 +71,7 @@ const wrapInBoundaryExtension = (element: JSX.Element) => {
|
||||
routeRef,
|
||||
path: '/',
|
||||
element: (
|
||||
<ExtensionBoundary id={id} source={source} routeRef={routeRef}>
|
||||
<ExtensionBoundary id={id} source={source}>
|
||||
{element}
|
||||
</ExtensionBoundary>
|
||||
),
|
||||
@@ -128,7 +126,7 @@ describe('ExtensionBoundary', () => {
|
||||
subject,
|
||||
context: {
|
||||
extension: 'plugin.extension',
|
||||
routeRef: 'plugin.extension',
|
||||
routeRef: 'unknown',
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -14,36 +14,59 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { ReactNode } from 'react';
|
||||
import { AnalyticsContext } from '@backstage/core-plugin-api';
|
||||
import React, { PropsWithChildren, ReactNode, useEffect } from 'react';
|
||||
import { AnalyticsContext, useAnalytics } from '@backstage/core-plugin-api';
|
||||
import { BackstagePlugin } from '../wiring';
|
||||
import { RouteRef } from '../routing';
|
||||
import { ErrorBoundary } from './ErrorBoundary';
|
||||
import { toInternalRouteRef } from '../routing/RouteRef';
|
||||
import { ExtensionSuspense } from './ExtensionSuspense';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { routableExtensionRenderedEvent } from '../../../core-plugin-api/src/analytics/Tracker';
|
||||
|
||||
type RouteTrackerProps = PropsWithChildren<{
|
||||
disableTracking?: boolean;
|
||||
}>;
|
||||
|
||||
const RouteTracker = (props: RouteTrackerProps) => {
|
||||
const { disableTracking, children } = props;
|
||||
const analytics = useAnalytics();
|
||||
|
||||
// This event, never exposed to end-users of the analytics API,
|
||||
// helps inform which extension metadata gets associated with a
|
||||
// navigation event when the route navigated to is a gathered
|
||||
// mountpoint.
|
||||
useEffect(() => {
|
||||
if (disableTracking) return;
|
||||
analytics.captureEvent(routableExtensionRenderedEvent, '');
|
||||
}, [analytics, disableTracking]);
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export interface ExtensionBoundaryProps {
|
||||
id: string;
|
||||
source?: BackstagePlugin;
|
||||
routeRef?: RouteRef;
|
||||
routable?: boolean;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function ExtensionBoundary(props: ExtensionBoundaryProps) {
|
||||
const { id, source, routeRef, children } = props;
|
||||
const { id, source, routable, children } = props;
|
||||
|
||||
// Skipping "routeRef" attribute in the new system, the extension "id" should provide more insight
|
||||
const attributes = {
|
||||
extension: id,
|
||||
pluginId: source?.id,
|
||||
routeRef: routeRef
|
||||
? toInternalRouteRef(routeRef).getDescription()
|
||||
: undefined,
|
||||
};
|
||||
|
||||
return (
|
||||
<ErrorBoundary plugin={source}>
|
||||
<AnalyticsContext attributes={attributes}>{children}</AnalyticsContext>
|
||||
</ErrorBoundary>
|
||||
<ExtensionSuspense>
|
||||
<ErrorBoundary plugin={source}>
|
||||
<AnalyticsContext attributes={attributes}>
|
||||
<RouteTracker disableTracking={!routable}>{children}</RouteTracker>
|
||||
</AnalyticsContext>
|
||||
</ErrorBoundary>
|
||||
</ExtensionSuspense>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,8 +18,3 @@ export {
|
||||
ExtensionBoundary,
|
||||
type ExtensionBoundaryProps,
|
||||
} from './ExtensionBoundary';
|
||||
|
||||
export {
|
||||
ExtensionSuspense,
|
||||
type ExtensionSuspenseProps,
|
||||
} from './ExtensionSuspense';
|
||||
|
||||
@@ -14,11 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { lazy, useEffect } from 'react';
|
||||
import { useAnalytics } from '@backstage/core-plugin-api';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { routableExtensionRenderedEvent } from '../../../core-plugin-api/src/analytics/Tracker';
|
||||
import { ExtensionBoundary, ExtensionSuspense } from '../components';
|
||||
import React, { lazy } from 'react';
|
||||
import { ExtensionBoundary } from '../components';
|
||||
import { createSchemaFromZod, PortableSchema } from '../schema';
|
||||
import {
|
||||
coreExtensionData,
|
||||
@@ -58,9 +55,7 @@ export function createPageExtension<
|
||||
}) => Promise<JSX.Element>;
|
||||
},
|
||||
): Extension<TConfig> {
|
||||
const { id, routeRef } = options;
|
||||
|
||||
const attachTo = options.attachTo ?? { id: 'core.routes', input: 'routes' };
|
||||
const { id } = options;
|
||||
|
||||
const configSchema =
|
||||
'configSchema' in options
|
||||
@@ -71,7 +66,7 @@ export function createPageExtension<
|
||||
|
||||
return createExtension({
|
||||
id,
|
||||
attachTo,
|
||||
attachTo: options.attachTo ?? { id: 'core.routes', input: 'routes' },
|
||||
configSchema,
|
||||
inputs: options.inputs,
|
||||
disabled: options.disabled,
|
||||
@@ -81,36 +76,18 @@ export function createPageExtension<
|
||||
routeRef: coreExtensionData.routeRef.optional(),
|
||||
},
|
||||
factory({ bind, config, inputs, source }) {
|
||||
const { path } = config;
|
||||
|
||||
const PageComponent = lazy(() =>
|
||||
const ExtensionComponent = lazy(() =>
|
||||
options
|
||||
.loader({ config, inputs })
|
||||
.then(element => ({ default: () => element })),
|
||||
);
|
||||
|
||||
const ExtensionComponent = () => {
|
||||
const analytics = useAnalytics();
|
||||
|
||||
// This event, never exposed to end-users of the analytics API,
|
||||
// helps inform which extension metadata gets associated with a
|
||||
// navigation event when the route navigated to is a gathered
|
||||
// mountpoint.
|
||||
useEffect(() => {
|
||||
analytics.captureEvent(routableExtensionRenderedEvent, '');
|
||||
}, [analytics]);
|
||||
|
||||
return <PageComponent />;
|
||||
};
|
||||
|
||||
bind({
|
||||
path,
|
||||
routeRef,
|
||||
path: config.path,
|
||||
routeRef: options.routeRef,
|
||||
element: (
|
||||
<ExtensionBoundary id={id} source={source} routeRef={routeRef}>
|
||||
<ExtensionSuspense>
|
||||
<ExtensionComponent />
|
||||
</ExtensionSuspense>
|
||||
<ExtensionBoundary id={id} source={source} routable>
|
||||
<ExtensionComponent />
|
||||
</ExtensionBoundary>
|
||||
),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user