Skip to content

Commit cc56909

Browse files
committed
feat(pipeline): Add completion view support to pipeline definitions
Add a completionView property to PipelineDefinition that allows pipelines to render a custom view after completion before firing onComplete. When set, onComplete is deferred until the component calls finish(). When null, existing behavior is preserved. Adds JSDoc comments to PipelineDefinition and PipelineStepDefinition.
1 parent 42d5f41 commit cc56909

File tree

7 files changed

+49
-2
lines changed

7 files changed

+49
-2
lines changed

static/app/components/pipeline/pipelineDummyProvider.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export const dummyIntegrationPipeline = {
6868
provider: 'dummy',
6969
actionTitle: t('Dummy Integration Pipeline'),
7070
getCompletionData: pipelineComplete<DummyCompletionData>,
71+
completionView: null,
7172
steps: [
7273
{
7374
stepId: 'step_one',

static/app/components/pipeline/pipelineIntegrationBitbucket.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export const bitbucketIntegrationPipeline = {
8686
provider: 'bitbucket',
8787
actionTitle: t('Installing Bitbucket Integration'),
8888
getCompletionData: pipelineComplete<IntegrationWithConfig>,
89+
completionView: null,
8990
steps: [
9091
{
9192
stepId: 'authorize',

static/app/components/pipeline/pipelineIntegrationGitHub.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ export const githubIntegrationPipeline = {
261261
provider: 'github',
262262
actionTitle: t('Installing GitHub Integration'),
263263
getCompletionData: pipelineComplete<IntegrationWithConfig>,
264+
completionView: null,
264265
steps: [
265266
{
266267
stepId: 'oauth_login',

static/app/components/pipeline/pipelineIntegrationGitLab.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ export const gitlabIntegrationPipeline = {
279279
provider: 'gitlab',
280280
actionTitle: t('Installing GitLab Integration'),
281281
getCompletionData: pipelineComplete<IntegrationWithConfig>,
282+
completionView: null,
282283
steps: [
283284
{
284285
stepId: 'installation_config',

static/app/components/pipeline/pipelineIntegrationSlack.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export const slackIntegrationPipeline = {
3636
provider: 'slack',
3737
actionTitle: t('Installing Slack Integration'),
3838
getCompletionData: pipelineComplete<IntegrationWithConfig>,
39+
completionView: null,
3940
steps: [
4041
{
4142
stepId: 'oauth_login',

static/app/components/pipeline/types.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ export interface PipelineStepDefinition<StepId extends string = string> {
2828
stepId: StepId;
2929
}
3030

31+
/**
32+
* Props passed to a pipeline's completion view component.
33+
*/
34+
export interface PipelineCompletionProps<D = unknown> {
35+
data: D;
36+
finish: () => void;
37+
}
38+
3139
/**
3240
* Defines a complete pipeline with its type, provider, and ordered steps.
3341
*/
@@ -39,6 +47,12 @@ export interface PipelineDefinition<
3947
* Title displayed in the pipeline modal header.
4048
*/
4149
actionTitle: string;
50+
/**
51+
* Component rendered after the pipeline completes. When set, `onComplete`
52+
* is deferred until the component calls `finish()`. When null, `onComplete`
53+
* fires immediately on completion.
54+
*/
55+
completionView: React.ComponentType<PipelineCompletionProps<any>> | null;
4256
/**
4357
* Casts the raw completion response to the typed completion data shape.
4458
* Use the {@link pipelineComplete} helper for this.

static/app/components/pipeline/usePipeline.tsx

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {
1212
import type {
1313
ApiPipeline,
1414
PipelineAdvanceResponse,
15+
PipelineCompletionProps,
1516
PipelineStepProps,
1617
PipelineStepResponse,
1718
} from './types';
@@ -189,7 +190,11 @@ export function usePipeline<
189190
const data = definition.getCompletionData(rawData) as CompletionDataFor<T, P>;
190191

191192
setState({status: 'complete', data});
192-
onCompleteRef.current?.(data);
193+
194+
// If there's no completion view, fire onComplete immediately.
195+
if (!definition.completionView) {
196+
onCompleteRef.current?.(data);
197+
}
193198
break;
194199
}
195200
case 'error':
@@ -237,7 +242,22 @@ export function usePipeline<
237242
return null;
238243
}, [state, definition]);
239244

245+
const finish = useCallback(() => {
246+
if (state.status === 'complete') {
247+
onCompleteRef.current?.(state.data);
248+
}
249+
}, [state]);
250+
240251
const view = useMemo(() => {
252+
// Render completion view when the pipeline is complete and one is defined
253+
if (state.status === 'complete' && definition.completionView) {
254+
// Widen from the specific union of component types to the general
255+
// CompletionView component type
256+
const CompletionView: React.ComponentType<PipelineCompletionProps<any>> =
257+
definition.completionView;
258+
return <CompletionView data={state.data} finish={finish} />;
259+
}
260+
241261
if (!stepDefinition) {
242262
return null;
243263
}
@@ -259,7 +279,15 @@ export function usePipeline<
259279
advanceError={advanceError}
260280
/>
261281
);
262-
}, [state, stepDefinition, definition, advanceMutate, isAdvancePending, advanceError]);
282+
}, [
283+
state,
284+
stepDefinition,
285+
definition,
286+
advanceMutate,
287+
isAdvancePending,
288+
advanceError,
289+
finish,
290+
]);
263291

264292
const {stepIndex, totalSteps} =
265293
state.status === 'active'

0 commit comments

Comments
 (0)