diff --git a/scm-ui/ui-extensions/src/ExtensionPoint.test.tsx b/scm-ui/ui-extensions/src/ExtensionPoint.test.tsx
index 3078c4c4d9..0b39817186 100644
--- a/scm-ui/ui-extensions/src/ExtensionPoint.test.tsx
+++ b/scm-ui/ui-extensions/src/ExtensionPoint.test.tsx
@@ -168,4 +168,46 @@ describe("ExtensionPoint test", () => {
const text = rendered.text();
expect(text).toBe("");
});
+
+ it("should render an instance", () => {
+ const Label = () => {
+ return ;
+ };
+
+ mockedBinder.hasExtension.mockReturnValue(true);
+ mockedBinder.getExtension.mockReturnValue();
+
+ const rendered = mount();
+ expect(rendered.text()).toBe("Extension One");
+ });
+
+ it("should render an instance with props", () => {
+ const Label = ({name}: {name: string}) => {
+ return ;
+ };
+
+ mockedBinder.hasExtension.mockReturnValue(true);
+ mockedBinder.getExtension.mockReturnValue();
+
+ const rendered = mount();
+ expect(rendered.text()).toBe("Extension Two");
+ });
+
+ it("should transform extension, before render", () => {
+ const label = ({name = "One"}: {name: string}) => {
+ return ;
+ };
+ mockedBinder.hasExtension.mockReturnValue(true);
+ mockedBinder.getExtension.mockReturnValue(label);
+
+ const transformer = (props: object) => {
+ return {
+ ...props,
+ name: "Two"
+ }
+ };
+
+ const rendered = mount();
+ expect(rendered.text()).toBe("Extension Two");
+ })
});
diff --git a/scm-ui/ui-extensions/src/ExtensionPoint.tsx b/scm-ui/ui-extensions/src/ExtensionPoint.tsx
index 0eb75509b1..aad9ca7b09 100644
--- a/scm-ui/ui-extensions/src/ExtensionPoint.tsx
+++ b/scm-ui/ui-extensions/src/ExtensionPoint.tsx
@@ -23,32 +23,44 @@
*/
import * as React from "react";
import { Binder } from "./binder";
-import { FC, ReactNode } from "react";
+import {Component, FC, ReactNode} from "react";
import useBinder from "./useBinder";
+type PropTransformer = (props: object) => object;
+
type Props = {
name: string;
renderAll?: boolean;
props?: object;
+ propTransformer?: PropTransformer;
};
-const renderAllExtensions = (binder: Binder, name: string, props?: object) => {
+const createInstance = (Component: any, props: object, key?: number) => {
+ const instanceProps = {
+ ...props,
+ key
+ };
+ if (React.isValidElement(Component)) {
+ return React.cloneElement(Component, instanceProps);
+ }
+ return ;
+};
+
+const renderAllExtensions = (binder: Binder, name: string, props: object) => {
const extensions = binder.getExtensions(name, props);
return (
<>
- {extensions.map((Component, index) => {
- return ;
- })}
+ {extensions.map((cmp, index) => createInstance(cmp, props, index))}
>
);
};
-const renderSingleExtension = (binder: Binder, name: string, props?: object) => {
- const Component = binder.getExtension(name, props);
- if (!Component) {
+const renderSingleExtension = (binder: Binder, name: string, props: object) => {
+ const cmp = binder.getExtension(name, props);
+ if (!cmp) {
return null;
}
- return ;
+ return createInstance(cmp, props, undefined);
};
const renderDefault = (children: ReactNode) => {
@@ -58,17 +70,29 @@ const renderDefault = (children: ReactNode) => {
return null;
};
+const createRenderProps = (propTransformer?: PropTransformer, props?: object) => {
+ const transform = (props: object) => {
+ if (!propTransformer) {
+ return props;
+ }
+ return propTransformer(props);
+ };
+
+ return transform(props || {});
+};
+
/**
* ExtensionPoint renders components which are bound to an extension point.
*/
-const ExtensionPoint: FC = ({ name, renderAll, props, children }) => {
+const ExtensionPoint: FC = ({ name, propTransformer, props, renderAll, children }) => {
const binder = useBinder();
- if (!binder.hasExtension(name, props)) {
+ const renderProps = createRenderProps(propTransformer, props);
+ if (!binder.hasExtension(name, renderProps)) {
return renderDefault(children);
} else if (renderAll) {
- return renderAllExtensions(binder, name, props);
+ return renderAllExtensions(binder, name, renderProps);
}
- return renderSingleExtension(binder, name, props);
+ return renderSingleExtension(binder, name, renderProps);
};
export default ExtensionPoint;