Merge pull request #14054 from backstage/blam/scaffolder-next-ready

feat(plugins/scaffolder/next): Re-use the old `TaskPage` for now for end-to-end experience
This commit is contained in:
Ben Lambert
2022-10-11 13:04:37 +01:00
committed by GitHub
6 changed files with 53 additions and 20 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder': patch
---
Make the `/next` scaffolder work end to end with the old `TaskPage` view
@@ -87,7 +87,6 @@ export const Router = (props: PropsWithChildren<NextRouterProps>) => {
/>
}
/>
<Route
path={nextSelectedTemplateRouteRef.path}
element={
@@ -30,7 +30,7 @@ describe('Stepper', () => {
};
const { getByText } = await renderInTestApp(
<Stepper manifest={manifest} extensions={[]} />,
<Stepper manifest={manifest} extensions={[]} onComplete={jest.fn()} />,
);
for (const step of manifest.steps) {
@@ -48,7 +48,7 @@ describe('Stepper', () => {
};
const { getByRole } = await renderInTestApp(
<Stepper manifest={manifest} extensions={[]} />,
<Stepper manifest={manifest} extensions={[]} onComplete={jest.fn()} />,
);
expect(getByRole('button', { name: 'Next' })).toBeInTheDocument();
@@ -88,7 +88,7 @@ describe('Stepper', () => {
};
const { getByRole } = await renderInTestApp(
<Stepper manifest={manifest} extensions={[]} />,
<Stepper manifest={manifest} extensions={[]} onComplete={jest.fn()} />,
);
await fireEvent.change(getByRole('textbox', { name: 'name' }), {
@@ -134,6 +134,7 @@ describe('Stepper', () => {
<Stepper
manifest={manifest}
extensions={[{ name: 'Mock', component: MockComponent }]}
onComplete={jest.fn()}
/>,
);
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { useApiHolder } from '@backstage/core-plugin-api';
import { JsonObject } from '@backstage/types';
import { JsonObject, JsonValue } from '@backstage/types';
import {
Stepper as MuiStepper,
Step as MuiStep,
@@ -50,6 +50,7 @@ const useStyles = makeStyles(theme => ({
export interface StepperProps {
manifest: TemplateParameterSchema;
extensions: NextFieldExtensionOptions<any, any>[];
onComplete: (values: Record<string, JsonValue>) => Promise<void>;
}
// TODO(blam): We require here, as the types in this package depend on @rjsf/core explicitly
@@ -109,10 +110,6 @@ export const Stepper = (props: StepperProps) => {
setFormState(current => ({ ...current, ...formData }));
};
const handleCreate = () => {
// TODO(blam): Create the template in a modal with the ability to view the logs etc.
};
return (
<>
<MuiStepper activeStep={activeStep} alternativeLabel variant="elevation">
@@ -161,7 +158,10 @@ export const Stepper = (props: StepperProps) => {
>
Back
</Button>
<Button variant="contained" onClick={handleCreate}>
<Button
variant="contained"
onClick={() => props.onComplete(formState)}
>
Create
</Button>
</div>
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { useEffect } from 'react';
import React, { useContext, useEffect } from 'react';
import {
Page,
Header,
@@ -23,7 +23,7 @@ import {
MarkdownContent,
} from '@backstage/core-components';
import { NextFieldExtensionOptions } from '../../extensions';
import { Navigate } from 'react-router';
import { Navigate, useNavigate } from 'react-router';
import { stringifyEntityRef } from '@backstage/catalog-model';
import {
errorApiRef,
@@ -36,7 +36,13 @@ import useAsync from 'react-use/lib/useAsync';
import { makeStyles } from '@material-ui/core';
import { Stepper } from './Stepper';
import { BackstageTheme } from '@backstage/theme';
import { nextRouteRef, selectedTemplateRouteRef } from '../../routes';
import {
nextRouteRef,
scaffolderTaskRouteRef,
selectedTemplateRouteRef,
} from '../../routes';
import { SecretsContext } from '../../components/secrets/SecretsContext';
import { JsonValue } from '@backstage/types';
export interface TemplateWizardPageProps {
customFieldExtensions: NextFieldExtensionOptions<any, any>[];
@@ -67,17 +73,32 @@ const useTemplateParameterSchema = (templateRef: string) => {
export const TemplateWizardPage = (props: TemplateWizardPageProps) => {
const styles = useStyles();
const rootRef = useRouteRef(nextRouteRef);
const taskRoute = useRouteRef(scaffolderTaskRouteRef);
const { secrets } = useContext(SecretsContext) ?? {};
const scaffolderApi = useApi(scaffolderApiRef);
const navigate = useNavigate();
const { templateName, namespace } = useRouteRefParams(
selectedTemplateRouteRef,
);
const templateRef = stringifyEntityRef({
kind: 'Template',
namespace,
name: templateName,
});
const errorApi = useApi(errorApiRef);
const { loading, manifest, error } = useTemplateParameterSchema(
stringifyEntityRef({
kind: 'Template',
namespace,
name: templateName,
}),
);
const { loading, manifest, error } = useTemplateParameterSchema(templateRef);
const onComplete = async (values: Record<string, JsonValue>) => {
const { taskId } = await scaffolderApi.scaffold({
templateRef,
values,
secrets,
});
navigate(taskRoute({ taskId }));
};
useEffect(() => {
if (error) {
@@ -113,6 +134,7 @@ export const TemplateWizardPage = (props: TemplateWizardPageProps) => {
<Stepper
manifest={manifest}
extensions={props.customFieldExtensions}
onComplete={onComplete}
/>
</InfoCard>
)}
+6
View File
@@ -65,6 +65,12 @@ export const scaffolderTaskRouteRef = createSubRouteRef({
path: '/tasks/:taskId',
});
export const nextScaffolderTaskRouteRef = createSubRouteRef({
id: 'scaffolder/next/task',
parent: nextRouteRef,
path: '/tasks/:taskId',
});
export const scaffolderListTaskRouteRef = createSubRouteRef({
id: 'scaffolder/list-tasks',
parent: rootRouteRef,