Create new package for plugin with react components

Signed-off-by: Malikah Montgomery <malikah.montgomery@thoughtworks.com>
This commit is contained in:
Malikah Montgomery
2023-05-15 10:25:10 -04:00
parent 6c0867be8d
commit 3f11ae24ab
9 changed files with 686 additions and 1 deletions
@@ -0,0 +1,46 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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 {
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
} from '@material-ui/core';
/** @public */
export const SettingsModal = (props: {
open: boolean;
close: Function;
componentName: string;
children: JSX.Element;
}) => {
const { open, close, componentName, children } = props;
return (
<Dialog open={open} onClose={() => close()}>
<DialogTitle>Settings - {componentName}</DialogTitle>
<DialogContent>{children}</DialogContent>
<DialogActions>
<Button onClick={() => close()} color="primary" variant="contained">
Close
</Button>
</DialogActions>
</Dialog>
);
};
@@ -0,0 +1,17 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 { SettingsModal } from './SettingsModal';
+194
View File
@@ -0,0 +1,194 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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, { Suspense } from 'react';
import { IconButton } from '@material-ui/core';
import SettingsIcon from '@material-ui/icons/Settings';
import { InfoCard } from '@backstage/core-components';
import { SettingsModal } from './components';
import { createReactExtension, useApp } from '@backstage/core-plugin-api';
import { RJSFSchema } from '@rjsf/utils';
/**
* @public
*/
export type ComponentRenderer = {
Renderer?: (props: RendererProps) => JSX.Element;
};
/**
* @public
*/
export type ComponentParts = {
Content: (props?: any) => JSX.Element;
Actions?: () => JSX.Element;
Settings?: () => JSX.Element;
ContextProvider?: (props: any) => JSX.Element;
};
/**
* @public
*/
export type RendererProps = { title: string } & ComponentParts;
/**
* @public
*/
export type CardExtensionProps<T> = ComponentRenderer & { title?: string } & T;
/**
* @public
*/
export type CardLayout = {
width?: { minColumns?: number; maxColumns?: number; defaultColumns?: number };
height?: { minRows?: number; maxRows?: number; defaultRows?: number };
};
/**
* @public
*/
export type CardSettings = {
schema?: RJSFSchema;
};
/**
* @public
*/
export type CardConfig = {
layout?: CardLayout;
settings?: CardSettings;
};
/**
* An extension creator to create card based components for the homepage
*
* @public
*/
export function createCardExtension<T>(options: {
title: string;
components: () => Promise<ComponentParts>;
name?: string;
description?: string;
layout?: CardLayout;
settings?: CardSettings;
}) {
const { title, components, name, description, layout, settings } = options;
// If widget settings schema is defined, we don't want to show the Settings icon or dialog
const isCustomizable = settings?.schema !== undefined;
return createReactExtension({
name,
data: { title, description, 'home.widget.config': { layout, settings } },
component: {
lazy: () =>
components().then(componentParts => {
return (props: CardExtensionProps<T>) => {
return (
<CardExtension
{...props}
{...componentParts}
title={props.title || title}
isCustomizable={isCustomizable}
/>
);
};
}),
},
});
}
type CardExtensionComponentProps<T> = CardExtensionProps<T> &
ComponentParts & {
title: string;
isCustomizable?: boolean;
overrideTitle?: string;
};
function CardExtension<T>(props: CardExtensionComponentProps<T>) {
const {
Renderer,
Content,
Settings,
Actions,
ContextProvider,
isCustomizable,
title,
...childProps
} = props;
const app = useApp();
const { Progress } = app.getComponents();
const [settingsOpen, setSettingsOpen] = React.useState(false);
if (Renderer) {
return (
<Suspense fallback={<Progress />}>
<Renderer
title={title}
{...{
Content,
...(Actions ? { Actions } : {}),
...(Settings && !isCustomizable ? { Settings } : {}),
...(ContextProvider ? { ContextProvider } : {}),
...childProps,
}}
/>
</Suspense>
);
}
const cardProps = {
title: title,
...(Settings && !isCustomizable
? {
action: (
<IconButton onClick={() => setSettingsOpen(true)}>
<SettingsIcon>Settings</SettingsIcon>
</IconButton>
),
}
: {}),
...(Actions
? {
actions: <Actions />,
}
: {}),
};
const innerContent = (
<InfoCard {...cardProps}>
{Settings && !isCustomizable && (
<SettingsModal
open={settingsOpen}
componentName={title}
close={() => setSettingsOpen(false)}
>
<Settings />
</SettingsModal>
)}
<Content {...childProps} />
</InfoCard>
);
return (
<Suspense fallback={<Progress />}>
{ContextProvider ? (
<ContextProvider {...childProps}>{innerContent}</ContextProvider>
) : (
innerContent
)}
</Suspense>
);
}
+31
View File
@@ -0,0 +1,31 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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.
*/
/**
* A Backstage plugin that helps you build a home page
*
* @packageDocumentation
*/
export * from './components';
export { createCardExtension } from './extensions';
export type {
CardExtensionProps,
ComponentParts,
ComponentRenderer,
RendererProps,
CardLayout,
CardSettings,
} from './extensions';