diff --git a/.changeset/yellow-rules-vanish.md b/.changeset/yellow-rules-vanish.md new file mode 100644 index 0000000000..23fff893f6 --- /dev/null +++ b/.changeset/yellow-rules-vanish.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Use template title for ongoing task page header diff --git a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.test.tsx b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.test.tsx index d62ef6f95e..7a04854c6f 100644 --- a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.test.tsx +++ b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.test.tsx @@ -17,9 +17,9 @@ import { OngoingTask } from './OngoingTask'; import React from 'react'; import { + mockApis, renderInTestApp, TestApiProvider, - mockApis, } from '@backstage/test-utils'; import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react'; import { act, fireEvent, waitFor, within } from '@testing-library/react'; @@ -30,6 +30,7 @@ import { import { rootRouteRef } from '../../routes'; import { AuthorizeResult } from '@backstage/plugin-permission-common'; import { SWRConfig } from 'swr'; +import { entityPresentationApiRef } from '@backstage/plugin-catalog-react'; jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), @@ -47,7 +48,10 @@ jest.mock('@backstage/plugin-scaffolder-react', () => ({ task: { spec: { steps: [], - templateInfo: { entity: { metadata: { name: 'my-template' } } }, + templateInfo: { + entityRef: 'template:default/my-template', + entity: { metadata: { name: 'my-template' } }, + }, }, }, }), @@ -59,6 +63,12 @@ describe('OngoingTask', () => { getTask: jest.fn().mockImplementation(async () => {}), }; + const mockEntityPresentationApi = { + forEntity: jest.fn().mockReturnValue({ + promise: new Promise(resolve => resolve({ primaryTitle: 'My template' })), + }), + }; + beforeEach(async () => { jest.clearAllMocks(); }); @@ -71,6 +81,7 @@ describe('OngoingTask', () => { apis={[ [scaffolderApiRef, mockScaffolderApi], [permissionApiRef, permissionApi || mockApis.permission()], + [entityPresentationApiRef, mockEntityPresentationApi], ]} > @@ -79,6 +90,12 @@ describe('OngoingTask', () => { { mountedRoutes: { '/': rootRouteRef } }, ); }; + + it('should render title', async () => { + const rendered = await render(); + expect(rendered.getByText('My template')).toBeInTheDocument(); + }); + it('should trigger cancel api on "Cancel" click in context menu', async () => { const rendered = await render(); const cancelOptionLabel = 'Cancel'; diff --git a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx index 97047d00c9..06620e7280 100644 --- a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx +++ b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx @@ -21,8 +21,8 @@ import Button from '@material-ui/core/Button'; import Paper from '@material-ui/core/Paper'; import { makeStyles } from '@material-ui/core/styles'; import { - ScaffolderTaskOutput, scaffolderApiRef, + ScaffolderTaskOutput, useTaskEventStream, } from '@backstage/plugin-scaffolder-react'; import { selectedTemplateRouteRef } from '../../routes'; @@ -38,11 +38,13 @@ import { useAsync } from '@react-hookz/web'; import { usePermission } from '@backstage/plugin-permission-react'; import { taskCancelPermission, - taskReadPermission, taskCreatePermission, + taskReadPermission, } from '@backstage/plugin-scaffolder-common/alpha'; import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; import { scaffolderTranslationRef } from '../../translation'; +import { entityPresentationApiRef } from '@backstage/plugin-catalog-react'; +import { default as reactUseAsync } from 'react-use/esm/useAsync'; const useStyles = makeStyles(theme => ({ contentWrapper: { @@ -79,6 +81,7 @@ export const OngoingTask = (props: { const navigate = useNavigate(); const analytics = useAnalytics(); const scaffolderApi = useApi(scaffolderApiRef); + const entityPresentationApi = useApi(entityPresentationApiRef); const taskStream = useTaskEventStream(taskId!); const classes = useStyles(); const steps = useMemo( @@ -123,6 +126,14 @@ export const OngoingTask = (props: { } }, [taskStream.error, taskStream.completed]); + const { value: presentation } = reactUseAsync(async () => { + const templateEntityRef = taskStream.task?.spec.templateInfo?.entityRef; + if (!templateEntityRef) { + return undefined; + } + return entityPresentationApi.forEntity(templateEntityRef).promise; + }, [entityPresentationApi, taskStream.task?.spec.templateInfo?.entityRef]); + const activeStep = useMemo(() => { for (let i = steps.length - 1; i >= 0; i--) { if (steps[i].status !== 'open') { @@ -184,22 +195,22 @@ export const OngoingTask = (props: { const Outputs = props.TemplateOutputsComponent ?? DefaultTemplateOutputs; - const templateName = - taskStream.task?.spec.templateInfo?.entity?.metadata.name || ''; - const cancelEnabled = !(taskStream.cancelled || taskStream.completed); return (
- {t('ongoingTask.title')} {templateName} + {t('ongoingTask.title')}{' '} + {presentation ? presentation.primaryTitle : ''} } subtitle={t('ongoingTask.subtitle', { taskId: taskId as string })}