Skip to content

Commit 73611a4

Browse files
authored
render restart success message with link (#858)
1 parent 4a1460d commit 73611a4

File tree

9 files changed

+146
-25
lines changed

9 files changed

+146
-25
lines changed

src/components/link/link.styles.ts

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,53 @@
1-
import { withStyle } from 'baseui';
1+
import { type Theme, withStyle } from 'baseui';
22
import { StyledLink } from 'baseui/link';
33

4+
import { type Props } from './link.types';
5+
46
export const styled = {
57
LinkBase: withStyle<typeof StyledLink, { disabled: boolean }>(
68
StyledLink,
7-
({ $theme }) => ({
8-
'[disabled]': {
9-
pointerEvents: 'none',
10-
color: `${$theme.colors.contentStateDisabled} !important`,
11-
},
12-
':visited': {
13-
color: 'inherit',
14-
},
15-
})
9+
({ $theme, color }: { $theme: Theme; color?: Props['color'] }) => {
10+
let effectiveColors:
11+
| {
12+
linkText: string;
13+
linkVisited: string;
14+
linkHover: string;
15+
linkActive: string;
16+
}
17+
| undefined = undefined;
18+
19+
if (color === 'contentPrimary' || !color) {
20+
effectiveColors = {
21+
linkText: $theme.colors.linkText,
22+
linkVisited: $theme.colors.linkText, // keep visited color same as original color
23+
linkHover: $theme.colors.linkHover,
24+
linkActive: $theme.colors.linkActive,
25+
};
26+
} else if (color === 'contentInversePrimary') {
27+
effectiveColors = {
28+
linkText: $theme.colors.contentInversePrimary,
29+
linkVisited: $theme.colors.contentInversePrimary, // keep visited color same as original color
30+
linkHover: $theme.colors.contentInverseSecondary,
31+
linkActive: $theme.colors.contentInverseSecondary,
32+
};
33+
}
34+
35+
return {
36+
'[disabled]': {
37+
pointerEvents: 'none',
38+
color: `${$theme.colors.contentStateDisabled} !important`,
39+
},
40+
':visited': {
41+
color: effectiveColors?.linkVisited || color,
42+
},
43+
':hover': {
44+
color: effectiveColors?.linkHover || color,
45+
},
46+
':active': {
47+
color: effectiveColors?.linkActive || color,
48+
},
49+
color: effectiveColors?.linkText || color,
50+
};
51+
}
1652
),
1753
};

src/components/link/link.types.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import type React from 'react';
22

3+
import { type StyledLink } from 'baseui/link';
34
import type NextLink from 'next/link';
45

5-
export type Props = React.ComponentProps<typeof NextLink>;
6+
type LinkProps = React.ComponentProps<typeof NextLink> &
7+
React.ComponentProps<typeof StyledLink>;
8+
9+
export type Props = Omit<LinkProps, 'color'> & {
10+
color?: LinkProps['color'] | 'contentPrimary' | 'contentInversePrimary';
11+
};

src/views/workflow-actions/__fixtures__/workflow-actions-config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const mockWorkflowActionsConfig: [
2323
icon: MdHighlightOff,
2424
getIsRunnable: () => true,
2525
apiRoute: 'cancel',
26-
getSuccessMessage: () => 'Mock cancel notification',
26+
renderSuccessMessage: () => 'Mock cancel notification',
2727
},
2828
{
2929
id: 'terminate',
@@ -39,6 +39,6 @@ export const mockWorkflowActionsConfig: [
3939
icon: MdPowerSettingsNew,
4040
getIsRunnable: () => false,
4141
apiRoute: 'terminate',
42-
getSuccessMessage: () => 'Mock terminate notification',
42+
renderSuccessMessage: () => 'Mock terminate notification',
4343
},
4444
] as const;

src/views/workflow-actions/config/workflow-actions.config.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { createElement } from 'react';
2+
13
import {
24
MdHighlightOff,
35
MdPowerSettingsNew,
@@ -9,6 +11,7 @@ import { type RestartWorkflowResponse } from '@/route-handlers/restart-workflow/
911
import { type TerminateWorkflowResponse } from '@/route-handlers/terminate-workflow/terminate-workflow.types';
1012

1113
import getWorkflowIsCompleted from '../../workflow-page/helpers/get-workflow-is-completed';
14+
import WorkflowActionRestartSuccessMsg from '../workflow-action-restart-success-msg/workflow-action-restart-success-msg';
1215
import { type WorkflowAction } from '../workflow-actions.types';
1316

1417
const workflowActionsConfig: [
@@ -33,7 +36,7 @@ const workflowActionsConfig: [
3336
workflow.workflowExecutionInfo?.closeEvent?.attributes ?? ''
3437
),
3538
apiRoute: 'cancel',
36-
getSuccessMessage: () => 'Workflow cancellation has been requested.',
39+
renderSuccessMessage: () => 'Workflow cancellation has been requested.',
3740
},
3841
{
3942
id: 'terminate',
@@ -52,7 +55,7 @@ const workflowActionsConfig: [
5255
workflow.workflowExecutionInfo?.closeEvent?.attributes ?? ''
5356
),
5457
apiRoute: 'terminate',
55-
getSuccessMessage: () => 'Workflow has been terminated.',
58+
renderSuccessMessage: () => 'Workflow has been terminated.',
5659
},
5760
{
5861
id: 'restart',
@@ -67,9 +70,8 @@ const workflowActionsConfig: [
6770
icon: MdOutlineRestartAlt,
6871
getIsRunnable: () => true,
6972
apiRoute: 'restart',
70-
getSuccessMessage: (result) =>
71-
// TODO: change runid to a link (upcomming PR)
72-
`Workflow has been restarted with new run ID: ${result.runId}`,
73+
renderSuccessMessage: (props) =>
74+
createElement(WorkflowActionRestartSuccessMsg, props),
7375
},
7476
] as const;
7577

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { render, screen } from '@testing-library/react';
2+
3+
import { type RestartWorkflowResponse } from '@/route-handlers/restart-workflow/restart-workflow.types';
4+
5+
import WorkflowActionRestartSuccessMsg from '../workflow-action-restart-success-msg';
6+
7+
describe('WorkflowActionRestartSuccessMsg', () => {
8+
const mockProps = {
9+
result: {
10+
runId: 'test-run-id',
11+
} as RestartWorkflowResponse,
12+
inputParams: {
13+
domain: 'test-domain',
14+
cluster: 'test-cluster',
15+
workflowId: 'test-workflow-id',
16+
runId: 'test-run-id',
17+
},
18+
};
19+
20+
it('renders the success message with a link', () => {
21+
render(<WorkflowActionRestartSuccessMsg {...mockProps} />);
22+
23+
expect(
24+
screen.getByText(/Workflow has been restarted[\.]/)
25+
).toBeInTheDocument();
26+
expect(screen.getByText('Click here')).toBeInTheDocument();
27+
expect(
28+
screen.getByText(/to view the new workflow[\.]/)
29+
).toBeInTheDocument();
30+
});
31+
32+
it('renders the link with the correct href', () => {
33+
render(<WorkflowActionRestartSuccessMsg {...mockProps} />);
34+
const { domain, cluster, workflowId, runId } = mockProps.inputParams;
35+
const link = screen.getByText('Click here');
36+
expect(link).toHaveAttribute(
37+
'href',
38+
`/domains/${domain}/${cluster}/workflows/${workflowId}/${runId}`
39+
);
40+
});
41+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import Link from '@/components/link/link';
2+
import { type RestartWorkflowResponse } from '@/route-handlers/restart-workflow/restart-workflow.types';
3+
4+
import { type WorkflowActionSuccessMessageProps } from '../workflow-actions.types';
5+
6+
const WorkflowActionRestartSuccessMsg = ({
7+
result: { runId },
8+
inputParams: { workflowId, cluster, domain },
9+
}: WorkflowActionSuccessMessageProps<RestartWorkflowResponse>) => {
10+
return (
11+
<>
12+
Workflow has been restarted.{' '}
13+
<Link
14+
color="contentInversePrimary"
15+
href={`/domains/${domain}/${cluster}/workflows/${workflowId}/${runId}`}
16+
>
17+
Click here
18+
</Link>{' '}
19+
to view the new workflow.
20+
</>
21+
);
22+
};
23+
24+
export default WorkflowActionRestartSuccessMsg;

src/views/workflow-actions/workflow-actions-modal-content/workflow-actions-modal-content.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@ export default function WorkflowActionsModalContent<R>({
5353

5454
onCloseModal();
5555
enqueue({
56-
message: action.getSuccessMessage(result, params),
56+
message: action.renderSuccessMessage?.({
57+
result,
58+
inputParams: params,
59+
}),
5760
startEnhancer: MdCheckCircle,
5861
actionMessage: 'OK',
5962
actionOnClick: () => dequeue(),

src/views/workflow-actions/workflow-actions.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,14 @@ export default function WorkflowActions() {
5959
<StatefulPopover
6060
placement={POPOVER_PLACEMENT.bottomRight}
6161
overrides={overrides.popover}
62-
content={() => (
62+
content={({ close }) => (
6363
<WorkflowActionsMenu
6464
workflow={workflow}
6565
actionsEnabledConfig={actionsEnabledConfig}
66-
onActionSelect={(action) => setSelectedAction(action)}
66+
onActionSelect={(action) => {
67+
setSelectedAction(action);
68+
close();
69+
}}
6770
/>
6871
)}
6972
returnFocus

src/views/workflow-actions/workflow-actions.types.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { type ReactNode } from 'react';
2+
13
import { type IconProps } from 'baseui/icon';
24

35
import { type DescribeWorkflowResponse } from '@/route-handlers/describe-workflow/describe-workflow.types';
@@ -12,6 +14,11 @@ export type WorkflowActionInputParams = {
1214

1315
export type WorkflowActionID = 'cancel' | 'terminate' | 'restart';
1416

17+
export type WorkflowActionSuccessMessageProps<R> = {
18+
result: R;
19+
inputParams: WorkflowActionInputParams;
20+
};
21+
1522
export type WorkflowAction<R> = {
1623
id: WorkflowActionID;
1724
label: string;
@@ -29,8 +36,7 @@ export type WorkflowAction<R> = {
2936
}>;
3037
getIsRunnable: (workflow: DescribeWorkflowResponse) => boolean;
3138
apiRoute: string;
32-
getSuccessMessage: (
33-
result: R,
34-
inputParams: WorkflowActionInputParams
35-
) => string;
39+
renderSuccessMessage: (
40+
props: WorkflowActionSuccessMessageProps<R>
41+
) => ReactNode;
3642
};

0 commit comments

Comments
 (0)