chore: starting to wire together a TaskPage for the router
Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
@@ -28,8 +28,13 @@ import {
|
||||
import { useElementFilter } from '@backstage/core-plugin-api';
|
||||
import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common';
|
||||
import { TemplateGroupFilter } from '../TemplateListPage/TemplateGroups';
|
||||
import { nextSelectedTemplateRouteRef } from '../../routes';
|
||||
import {
|
||||
nextScaffolderTaskRouteRef,
|
||||
nextSelectedTemplateRouteRef,
|
||||
} from '../../routes';
|
||||
import { SecretsContextProvider } from '../../components/secrets/SecretsContext';
|
||||
import { TaskPage } from '../TaskPage';
|
||||
import { ErrorPage } from '@backstage/core-components';
|
||||
|
||||
/**
|
||||
* The Props for the Scaffolder Router
|
||||
@@ -87,7 +92,6 @@ export const Router = (props: PropsWithChildren<NextRouterProps>) => {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path={nextSelectedTemplateRouteRef.path}
|
||||
element={
|
||||
@@ -96,6 +100,11 @@ export const Router = (props: PropsWithChildren<NextRouterProps>) => {
|
||||
</SecretsContextProvider>
|
||||
}
|
||||
/>
|
||||
<Route path={nextScaffolderTaskRouteRef.path} element={<TaskPage />} />
|
||||
<Route
|
||||
path="*"
|
||||
element={<ErrorPage status="404" statusMessage="Page not found" />}
|
||||
/>
|
||||
</Routes>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2022 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 { Page, Header, Content } from '@backstage/core-components';
|
||||
|
||||
export const TaskPage = () => {
|
||||
return (
|
||||
<Page themeId="website">
|
||||
<Header
|
||||
pageTitleOverride="Task ID"
|
||||
title="View Task"
|
||||
subtitle="View the status of a task"
|
||||
/>
|
||||
<Content>
|
||||
<p>Hey</p>
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2022 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 { TaskPage } from './TaskPage';
|
||||
@@ -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()}
|
||||
/>,
|
||||
);
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { useApiHolder } from '@backstage/core-plugin-api';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { useApi, useApiHolder } from '@backstage/core-plugin-api';
|
||||
import { JsonObject, JsonValue } from '@backstage/types';
|
||||
import {
|
||||
Stepper as MuiStepper,
|
||||
Step as MuiStep,
|
||||
@@ -31,6 +31,7 @@ import { createAsyncValidators } from './createAsyncValidators';
|
||||
import { useTemplateSchema } from './useTemplateSchema';
|
||||
import { ReviewState } from './ReviewState';
|
||||
import validator from '@rjsf/validator-ajv8';
|
||||
import { scaffolderApiRef } from '../../../api';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
backButton: {
|
||||
@@ -50,6 +51,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 +111,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 +159,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,
|
||||
nextScaffolderTaskRouteRef,
|
||||
selectedTemplateRouteRef,
|
||||
} from '../../routes';
|
||||
import { SecretsContext } from '../../components/secrets/SecretsContext';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
|
||||
export interface TemplateWizardPageProps {
|
||||
customFieldExtensions: NextFieldExtensionOptions<any, any>[];
|
||||
@@ -67,17 +73,31 @@ const useTemplateParameterSchema = (templateRef: string) => {
|
||||
export const TemplateWizardPage = (props: TemplateWizardPageProps) => {
|
||||
const styles = useStyles();
|
||||
const rootRef = useRouteRef(nextRouteRef);
|
||||
const nextTemplateRef = useRouteRef(nextScaffolderTaskRouteRef);
|
||||
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(nextTemplateRef({ taskId }));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
@@ -113,6 +133,7 @@ export const TemplateWizardPage = (props: TemplateWizardPageProps) => {
|
||||
<Stepper
|
||||
manifest={manifest}
|
||||
extensions={props.customFieldExtensions}
|
||||
onComplete={onComplete}
|
||||
/>
|
||||
</InfoCard>
|
||||
)}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user