@backstage/plugin-scaffolder-react fixes

Signed-off-by: Paul Cowan <paul.cowan@cutting.scot>
This commit is contained in:
Paul Cowan
2023-01-12 14:04:09 +00:00
parent 4ff036bd6b
commit f19d51fa4d
8 changed files with 31 additions and 37 deletions
@@ -20,19 +20,16 @@ import {
FIELD_EXTENSION_WRAPPER_KEY,
} from '../extensions/keys';
type Extension = Omit<FieldExtensionOptions, 'validation'> & { validation?: unknown }
/**
* Hook that returns all custom field extensions from the current outlet.
* @public
*/
export const useCustomFieldExtensions = <
TComponentDataType = FieldExtensionOptions
TComponentDataType = FieldExtensionOptions,
>(
outlet: React.ReactNode,
defaultScaffolderFieldExtensions: Extension[] = []
) => {
const customFieldExtensions = useElementFilter(outlet, elements =>
return useElementFilter(outlet, elements =>
elements
.selectByComponentData({
key: FIELD_EXTENSION_WRAPPER_KEY,
@@ -40,15 +37,5 @@ export const useCustomFieldExtensions = <
.findComponentData<TComponentDataType>({
key: FIELD_EXTENSION_KEY,
}),
) as FieldExtensionOptions[];
return [
...customFieldExtensions,
...defaultScaffolderFieldExtensions.filter(
({ name }) =>
!customFieldExtensions.some(
customFieldExtension => customFieldExtension.name === name,
),
),
];
);
};
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useAnalytics, useApiHolder, useRouteRefParams } from '@backstage/core-plugin-api';
import { useAnalytics, useApiHolder } from '@backstage/core-plugin-api';
import { JsonValue } from '@backstage/types';
import {
Stepper as MuiStepper,
@@ -34,7 +34,6 @@ import { useTemplateSchema } from '../../hooks/useTemplateSchema';
import { useFormDataFromQuery } from '../../hooks/useFormDataFromQuery';
import validator from '@rjsf/validator-ajv6';
const useStyles = makeStyles(theme => ({
backButton: {
marginRight: theme.spacing(1),
@@ -64,7 +63,7 @@ export type StepperProps = {
onComplete: (values: Record<string, JsonValue>) => Promise<void>;
initialFormState?: Record<string, JsonValue>;
ReviewStateWrapper?: (props: ReviewStateProps) => JSX.Element;
}
};
// TODO(blam): We require here, as the types in this package depend on @rjsf/core explicitly
// which is what we're using here as the default types, it needs to depend on @rjsf/core-v5 because
+10 -1
View File
@@ -93,7 +93,16 @@ export const Router = (props: RouterProps) => {
const outlet = useOutlet();
const TaskPageElement = TaskPageComponent ?? TaskPage;
const fieldExtensions = useCustomFieldExtensions(outlet, DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS);
const customFieldExtensions = useCustomFieldExtensions(outlet);
const fieldExtensions = [
...customFieldExtensions,
...DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS.filter(
({ name }) =>
!customFieldExtensions.some(
customFieldExtension => customFieldExtension.name === name,
),
),
] as FieldExtensionOptions[];
// todo(blam): this should also be moved to a hook in -react
const customLayouts = useElementFilter(outlet, elements =>
-6
View File
@@ -49,16 +49,10 @@ export {
EmbeddedScaffolderWorkflow,
type EmbeddedScaffolderWorkflowProps,
type WorkflowProps,
type ReviewStateProps,
type ParsedTemplateSchema,
} from './next';
export type { NextRouterProps } from './next';
export type { TemplateGroupFilter } from './next';
export {
nextRouteRef,
nextScaffolderTaskRouteRef,
nextSelectedTemplateRouteRef,
type TemplateGroupFilter,
type NextRouterProps,
type FormProps,
} from './next';
@@ -23,8 +23,10 @@ import {
import { act, fireEvent } from '@testing-library/react';
import React from 'react';
import { EmbeddedScaffolderWorkflow } from './EmbeddedScaffolderWorkflow';
import { scaffolderApiRef } from '../../api';
import { ScaffolderApi } from '../../types';
import {
scaffolderApiRef,
type ScaffolderApi,
} from '@backstage/plugin-scaffolder-react';
import { analyticsApiRef } from '@backstage/core-plugin-api';
const scaffolderApiMock: jest.Mocked<ScaffolderApi> = {
@@ -80,6 +82,7 @@ describe('<EmbeddedScaffolderWorkflow />', () => {
initialFormState={{
name: 'prefilled-name',
}}
customFieldExtensions={[]}
frontPage={
<>
<h1>Security Insights</h1>
@@ -18,22 +18,21 @@ import React, { useState, useCallback } from 'react';
import type { ReactNode } from 'react';
import type { JsonValue } from '@backstage/types';
import { EmbeddableWorkflow, type WorkflowProps } from '../Workflow/Workflow';
import { FormProps, useCustomFieldExtensions } from '@backstage/plugin-scaffolder-react';
import { FormProps } from '@backstage/plugin-scaffolder-react';
import { Box, Button } from '@material-ui/core';
import { DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS } from '../../extensions/default';
/**
* @alpha
*/
export type EmbeddedScaffolderWorkflowProps = Omit<
WorkflowProps,
'customFieldExtensions' | 'onComplete'
'onComplete' | 'FormProps'
> & {
customExtensionsElement?: React.ReactNode;
initialFormState?: Record<string, JsonValue>;
onComplete: (values: Record<string, JsonValue>) => Promise<void>;
onError(error: Error | undefined): JSX.Element | null;
FormProps: FormProps
FormProps?: FormProps;
frontPage: ReactNode;
finishPage: ReactNode;
} & Partial<Pick<WorkflowProps, 'onComplete'>>;
@@ -51,7 +50,6 @@ type OnCompleteArgs = Parameters<WorkflowProps['onComplete']>[0];
export function EmbeddedScaffolderWorkflow({
namespace,
templateName,
customExtensionsElement = <></>,
frontPage,
finishPage,
onComplete = async (_values: OnCompleteArgs) => void 0,
@@ -60,9 +58,10 @@ export function EmbeddedScaffolderWorkflow({
description,
ReviewStateWrapper,
initialFormState,
customFieldExtensions,
FormProps: formProps = {},
}: EmbeddedScaffolderWorkflowProps): JSX.Element {
const [display, setDisplay] = useState<Display>('front');
const fieldExtensions = useCustomFieldExtensions(customExtensionsElement, DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS);
const startTemplate = useCallback(() => setDisplay('workflow'), []);
@@ -92,9 +91,10 @@ export function EmbeddedScaffolderWorkflow({
templateName={templateName}
onComplete={onWorkFlowComplete}
onError={onError}
customFieldExtensions={fieldExtensions}
customFieldExtensions={customFieldExtensions}
ReviewStateWrapper={ReviewStateWrapper}
initialFormState={initialFormState}
FormProps={formProps}
/>
),
finish: (
@@ -30,7 +30,8 @@ import {
import useAsync from 'react-use/lib/useAsync';
import { JsonValue } from '@backstage/types';
import { FormProps } from '@backstage/plugin-scaffolder-react';
import { nextRouteRef, scaffolderTaskRouteRef, selectedTemplateRouteRef } from '../../routes';
import { nextRouteRef } from '../routes';
import { scaffolderTaskRouteRef, selectedTemplateRouteRef } from '../../routes';
import { Header, Page } from '@backstage/core-components';
import { Workflow } from '../Workflow/Workflow';
@@ -67,6 +67,7 @@ export interface WorkflowProps {
export const Workflow = ({
ReviewStateWrapper = ReviewState,
FormProps = {},
...props
}: WorkflowProps): JSX.Element | null => {
const styles = useStyles();
@@ -111,7 +112,7 @@ export const Workflow = ({
manifest={manifest}
extensions={props.customFieldExtensions}
onComplete={props.onComplete}
FormProps={props.FormProps}
FormProps={FormProps}
initialFormState={props.initialFormState}
ReviewStateWrapper={ReviewStateWrapper}
/>