diff --git a/docs/en/development/ui-extensions.md b/docs/en/development/ui-extensions.md index 7f0821550f..b1490a7126 100644 --- a/docs/en/development/ui-extensions.md +++ b/docs/en/development/ui-extensions.md @@ -105,7 +105,7 @@ binder.bind("repo.avatar", GitAvatar, (props) => props.type === "git"); ``` ```javascript - + ``` ### Typings @@ -141,3 +141,75 @@ Negative Example: This code for example, would lead to a compile time type error because we made a typo in the `name` of the extension when binding it. If we had used the `bind` method without the type parameter, we would not have gotten an error but run into problems at runtime. + +### Children + +If an extension point defines children those children are propagated to the extensions as children prop e.g: + +```tsx +const MyExtension:FC = ({children}) => ( +
{children}
+) +const App = () => { + binder.bind("box", MyExtension); + return ( + +

Box Content

+
+ ); +} +``` + +The example above renders the following html code: + +```html +
+

Box Content

+
+``` + +An exception is when the extension already has a children property, this could be the case if jsx is directly bind. +This exception applies not only to the children property it applies to every property. +The example below renders `Ahoi`, because the property of the jsx overwrites the one from the extension point. + +```tsx +type Props = { + greeting: string; +} + +const GreetingExtension:FC = ({greeting}) => ( + <>{greeting} +); + +const App = () => { + binder.bind("greet", ); + return ; +}; +``` + +### Wrapper + +Sometimes it can be useful to allow plugin developers to wrap an existing component. +The `wrapper` property is exactly for this case, it allows to wrap an existing component with multiple extensions e.g.: + +```tsx +const Outer: FC = ({ children }) => ( + <>Outer -> {children} +); + +const Inner: FC = ({ children }) => ( + <>Outer -> {children} +); + +const App = () => { + binder.bind("wrapped", Outer); + binder.bind("wrapped", Inner); + return ( + + Children + + ); +} +``` + +The example above renders `Outer -> Inner -> Children`, because each extension is passed as children to the parent extension. diff --git a/gradle/changelog/ep_history_download.yaml b/gradle/changelog/ep_history_download.yaml new file mode 100644 index 0000000000..c35635baf6 --- /dev/null +++ b/gradle/changelog/ep_history_download.yaml @@ -0,0 +1,2 @@ +- type: Added + description: Extension points for source tree ([#1816](https://github.com/scm-manager/scm-manager/pull/1816)) diff --git a/scm-ui/ui-extensions/src/ExtensionPoint.test.tsx b/scm-ui/ui-extensions/src/ExtensionPoint.test.tsx index 0f627374bb..0794322c39 100644 --- a/scm-ui/ui-extensions/src/ExtensionPoint.test.tsx +++ b/scm-ui/ui-extensions/src/ExtensionPoint.test.tsx @@ -21,9 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -import React from "react"; +import React, { FC } from "react"; import ExtensionPoint from "./ExtensionPoint"; import { shallow, mount } from "enzyme"; +// eslint-disable-next-line no-restricted-imports import "@scm-manager/ui-tests/enzyme"; import binder from "./binder"; @@ -89,7 +90,7 @@ describe("ExtensionPoint test", () => { ); @@ -126,7 +127,7 @@ describe("ExtensionPoint test", () => { it("should pass the context of the parent component", () => { const UserContext = React.createContext({ - name: "anonymous" + name: "anonymous", }); type HelloProps = { @@ -148,7 +149,7 @@ describe("ExtensionPoint test", () => { return ( @@ -187,7 +188,7 @@ describe("ExtensionPoint test", () => { }; mockedBinder.hasExtension.mockReturnValue(true); - mockedBinder.getExtension.mockReturnValue(