From a434838cfba5fab14530d848bb4610bc18cf177a Mon Sep 17 00:00:00 2001 From: Laura Whitaker Date: Thu, 7 Aug 2025 09:33:39 -0600 Subject: [PATCH 1/4] Move actions out of SplitButton --- src/lib/components/workflow-actions.svelte | 181 ++++++++++++--------- src/lib/i18n/locales/en/workflows.ts | 1 + 2 files changed, 105 insertions(+), 77 deletions(-) diff --git a/src/lib/components/workflow-actions.svelte b/src/lib/components/workflow-actions.svelte index 49b17854c7..acf7c200c7 100644 --- a/src/lib/components/workflow-actions.svelte +++ b/src/lib/components/workflow-actions.svelte @@ -7,8 +7,11 @@ import SignalConfirmationModal from '$lib/components/workflow/client-actions/signal-confirmation-modal.svelte'; import TerminateConfirmationModal from '$lib/components/workflow/client-actions/terminate-confirmation-modal.svelte'; import UpdateConfirmationModal from '$lib/components/workflow/client-actions/update-confirmation-modal.svelte'; + import Button from '$lib/holocene/button.svelte'; import { MenuDivider, MenuItem } from '$lib/holocene/menu'; - import SplitButton from '$lib/holocene/split-button.svelte'; + import MenuButton from '$lib/holocene/menu/menu-button.svelte'; + import MenuContainer from '$lib/holocene/menu/menu-container.svelte'; + import Menu from '$lib/holocene/menu/menu.svelte'; import { translate } from '$lib/i18n/translate'; import { isCloud } from '$lib/stores/advanced-visibility'; import { coreUserStore } from '$lib/stores/core-user'; @@ -141,86 +144,110 @@ ]; -{#if isRunning} - (cancelConfirmationModalOpen = true)} - label={translate('workflows.request-cancellation')} - menuLabel={translate('workflows.workflow-actions')} - > - {#each workflowActions as { onClick, destructive, label, enabled, testId, description }} - {#if destructive} - - {/if} - + {#if isRunning} + + + - {label} - - {/each} - {#if !workflowCreateDisabled($page)} - - - goto( - routeForWorkflowStart({ - namespace, - workflowId: workflow.id, - taskQueue: workflow.taskQueue, - workflowType: workflow.name, - }), - )} - data-testid="start-workflow-button" + {translate('workflows.more-actions')} + + - {translate('workflows.start-workflow-like-this-one')} - - {/if} - -{:else} - (resetConfirmationModalOpen = true)} - label={translate('workflows.reset')} - menuLabel={translate('workflows.workflow-actions')} - > - - goto( - routeForWorkflowStart({ - namespace, - workflowId: workflow.id, - taskQueue: workflow.taskQueue, - workflowType: workflow.name, - }), - )} - disabled={workflowCreateDisabled($page)} - data-testid="start-workflow-button" + {#each workflowActions as { onClick, destructive, label, enabled, testId, description }} + {#if destructive} + + {/if} + + {label} + + {/each} + {#if !workflowCreateDisabled($page)} + + + goto( + routeForWorkflowStart({ + namespace, + workflowId: workflow.id, + taskQueue: workflow.taskQueue, + workflowType: workflow.name, + }), + )} + data-testid="start-workflow-button" + > + {translate('workflows.start-workflow-like-this-one')} + + {/if} + + + {:else} + + + - {translate('workflows.terminate-latest')} - - {/if} - -{/if} + {translate('workflows.more-actions')} + + + + goto( + routeForWorkflowStart({ + namespace, + workflowId: workflow.id, + taskQueue: workflow.taskQueue, + workflowType: workflow.name, + }), + )} + disabled={workflowCreateDisabled($page)} + data-testid="start-workflow-button" + > + {translate('workflows.start-workflow-like-this-one')} + + {#if terminateEnabled && next} + + (terminateConfirmationModalOpen = true)} + data-testid="terminate-button" + destructive + > + {translate('workflows.terminate-latest')} + + {/if} + + + {/if} + {#if resetEnabled} Date: Thu, 7 Aug 2025 09:34:02 -0600 Subject: [PATCH 2/4] Adjust responsiveness of actions --- src/lib/layouts/workflow-header.svelte | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/layouts/workflow-header.svelte b/src/lib/layouts/workflow-header.svelte index 3b128d5b8d..52cd58a2e3 100644 --- a/src/lib/layouts/workflow-header.svelte +++ b/src/lib/layouts/workflow-header.svelte @@ -102,15 +102,15 @@
-
+
-
+
-
+
Date: Thu, 7 Aug 2025 09:47:47 -0600 Subject: [PATCH 3/4] Update to svelte 5 --- src/lib/components/workflow-actions.svelte | 121 +++++++++++---------- 1 file changed, 65 insertions(+), 56 deletions(-) diff --git a/src/lib/components/workflow-actions.svelte b/src/lib/components/workflow-actions.svelte index acf7c200c7..7dfd88e8eb 100644 --- a/src/lib/components/workflow-actions.svelte +++ b/src/lib/components/workflow-actions.svelte @@ -28,88 +28,97 @@ import { workflowTerminateEnabled } from '$lib/utilities/workflow-terminate-enabled'; import { workflowUpdateEnabled } from '$lib/utilities/workflow-update-enabled'; - export let workflow: WorkflowExecution; - export let namespace: string; - export let cancelInProgress: boolean; - export let isRunning: boolean; - export let first: string | undefined = undefined; - export let next: string | undefined = undefined; + interface Props { + workflow: WorkflowExecution; + namespace: string; + cancelInProgress: boolean; + isRunning: boolean; + first?: string; + next?: string; + } + + let { workflow, namespace, cancelInProgress, isRunning, first, next }: Props = + $props(); - let cancelConfirmationModalOpen = false; - let terminateConfirmationModalOpen = false; - let resetConfirmationModalOpen = false; - let signalConfirmationModalOpen = false; - let updateConfirmationModalOpen = false; + let cancelConfirmationModalOpen = $state(false); + let terminateConfirmationModalOpen = $state(false); + let resetConfirmationModalOpen = $state(false); + let signalConfirmationModalOpen = $state(false); + let updateConfirmationModalOpen = $state(false); - let resetDescription: string; let coreUser = coreUserStore(); - $: cancelEnabled = workflowCancelEnabled( - $page.data.settings, - $coreUser, - namespace, + let cancelEnabled = $derived( + workflowCancelEnabled($page.data.settings, $coreUser, namespace), ); - $: signalEnabled = workflowSignalEnabled( - $page.data.settings, - $coreUser, - namespace, + let signalEnabled = $derived( + workflowSignalEnabled($page.data.settings, $coreUser, namespace), ); - $: updateEnabled = workflowUpdateEnabled( - $page.data.settings, - $coreUser, - namespace, + let updateEnabled = $derived( + workflowUpdateEnabled($page.data.settings, $coreUser, namespace), ); - $: terminateEnabled = workflowTerminateEnabled( - $page.data.settings, - $coreUser, - namespace, + let terminateEnabled = $derived( + workflowTerminateEnabled($page.data.settings, $coreUser, namespace), ); - $: resetAuthorized = workflowResetEnabled( - $page.data.settings, - $coreUser, - namespace, + let resetAuthorized = $derived( + workflowResetEnabled($page.data.settings, $coreUser, namespace), ); // https://github.com/temporalio/temporal/releases/tag/v1.27.1 - $: canResetWithPendingChildWorkflows = + let canResetWithPendingChildWorkflows = $derived( minimumVersionRequired('1.27.1', $temporalVersion) || - $isCloud || - workflow.pendingChildren.length === 0; + $isCloud || + workflow.pendingChildren.length === 0, + ); - $: resetEnabled = + let resetEnabled = $derived( resetAuthorized && - canResetWithPendingChildWorkflows && - $resetEvents.length > 0; - $: actionsDisabled = !resetEnabled && !signalEnabled && !terminateEnabled; - - let workflowActions: { - label: string; - onClick: () => void; - enabled: boolean; - testId: string; - destructive?: boolean; - description?: string; - }[]; + canResetWithPendingChildWorkflows && + $resetEvents.length > 0, + ); + let actionsDisabled = $derived( + !resetEnabled && !signalEnabled && !terminateEnabled, + ); - $: { + const getResetDescription = ({ + resetAuthorized, + canResetWithPendingChildWorkflows, + resetEvents, + }) => { if (!resetAuthorized) { - resetDescription = translate('workflows.reset-disabled-unauthorized'); + return translate('workflows.reset-disabled-unauthorized'); } else if (resetAuthorized && !canResetWithPendingChildWorkflows) { - resetDescription = translate('workflows.reset-disabled-pending-children'); + return translate('workflows.reset-disabled-pending-children'); } else if ( resetAuthorized && canResetWithPendingChildWorkflows && - $resetEvents.length === 0 + resetEvents.length === 0 ) { - resetDescription = translate('workflows.reset-disabled-no-events'); + return translate('workflows.reset-disabled-no-events'); } - } + return ''; + }; - $: workflowActions = [ + let resetDescription = $derived( + getResetDescription({ + resetAuthorized, + canResetWithPendingChildWorkflows, + resetEvents: $resetEvents, + }), + ); + + let workflowActions: { + label: string; + onClick: () => void; + enabled: boolean; + testId: string; + destructive?: boolean; + description?: string; + }[] = $derived([ { label: translate('workflows.reset'), onClick: () => (resetConfirmationModalOpen = true), @@ -141,7 +150,7 @@ ? '' : translate('workflows.terminate-disabled'), }, - ]; + ]);
From cad9703588c17873a928304c5ae190502a6c82ec Mon Sep 17 00:00:00 2001 From: Laura Whitaker Date: Thu, 7 Aug 2025 14:29:27 -0600 Subject: [PATCH 4/4] Fix tests --- .../integration/disable-write-actions.spec.ts | 18 +- tests/integration/workflow-actions.spec.ts | 183 ++++-------------- 2 files changed, 52 insertions(+), 149 deletions(-) diff --git a/tests/integration/disable-write-actions.spec.ts b/tests/integration/disable-write-actions.spec.ts index 0147ed4c3b..e4227a4728 100644 --- a/tests/integration/disable-write-actions.spec.ts +++ b/tests/integration/disable-write-actions.spec.ts @@ -14,10 +14,12 @@ test.describe('Enable write actions by default', () => { }); test('Cancel workflow button enabled', async ({ page }) => { - const workflowActionButton = page - .locator('#workflow-actions-primary-button') - .locator('visible=true'); - await expect(workflowActionButton).toBeEnabled(); + test.slow(); + const cancelButton = page.getByRole('button', { + name: 'Request Cancellation', + }); + await expect(cancelButton).toBeVisible(); + await expect(cancelButton).toBeEnabled(); }); }); @@ -29,10 +31,10 @@ test.describe('Disable write actions on workflow actions', () => { }); test('Cancel workflow button disabled', async ({ page }) => { - const workflowActionButton = page - .locator('#workflow-actions-primary-button') - .locator('visible=true'); - await expect(workflowActionButton).toBeDisabled(); + const cancelButton = page.getByRole('button', { + name: 'Request Cancellation', + }); + await expect(cancelButton).toBeDisabled(); }); }); diff --git a/tests/integration/workflow-actions.spec.ts b/tests/integration/workflow-actions.spec.ts index 4bd3b00c51..808c5e429b 100644 --- a/tests/integration/workflow-actions.spec.ts +++ b/tests/integration/workflow-actions.spec.ts @@ -39,14 +39,10 @@ test.describe('Workflow Actions for a Completed Workflow', () => { await mockSettingsApi(page, { DisableWriteActions: true }); await expect( - page.locator('#workflow-actions-menu-button').locator('visible=true'), + page.getByRole('button', { name: 'More Actions' }), ).toBeEnabled(); - await expect( - page - .locator('#workflow-actions-primary-button') - .locator('visible=true'), - ).toBeDisabled(); + await expect(page.getByRole('button', { name: 'Reset' })).toBeDisabled(); }); test('is disabled when reset is disabled via Settings API', async ({ @@ -55,14 +51,10 @@ test.describe('Workflow Actions for a Completed Workflow', () => { await mockSettingsApi(page, { WorkflowResetDisabled: true }); await expect( - page.locator('#workflow-actions-menu-button').locator('visible=true'), + page.getByRole('button', { name: 'More Actions' }), ).toBeEnabled(); - await expect( - page - .locator('#workflow-actions-primary-button') - .locator('visible=true'), - ).toBeDisabled(); + await expect(page.getByRole('button', { name: 'Reset' })).toBeDisabled(); }); test('allows reapplying signals after the reset point', async ({ @@ -71,19 +63,12 @@ test.describe('Workflow Actions for a Completed Workflow', () => { const requestPromise = page.waitForRequest(WORKFLOW_RESET_API); await expect( - page.locator('#workflow-actions-menu-button').locator('visible=true'), + page.getByRole('button', { name: 'More Actions' }), ).toBeEnabled(); - await expect( - page - .locator('#workflow-actions-primary-button') - .locator('visible=true'), - ).toBeEnabled(); + await expect(page.getByRole('button', { name: 'Reset' })).toBeEnabled(); - await page - .locator('#workflow-actions-primary-button') - .locator('visible=true') - .click(); + await page.getByRole('button', { name: 'Reset' }).click(); await page .getByTestId('workflow-reset-event-id-select-button') @@ -116,19 +101,12 @@ test.describe('Workflow Actions for a Completed Workflow', () => { const requestPromise = page.waitForRequest(WORKFLOW_RESET_API); await expect( - page.locator('#workflow-actions-menu-button').locator('visible=true'), + page.getByRole('button', { name: 'More Actions' }), ).toBeEnabled(); - await expect( - page - .locator('#workflow-actions-primary-button') - .locator('visible=true'), - ).toBeEnabled(); + await expect(page.getByRole('button', { name: 'Reset' })).toBeEnabled(); - await page - .locator('#workflow-actions-primary-button') - .locator('visible=true') - .click(); + await page.getByRole('button', { name: 'Reset' }).click(); await page .getByTestId('workflow-reset-event-id-select-button') @@ -177,19 +155,12 @@ test.describe('Workflow Actions for a Completed Workflow', () => { const requestPromise = page.waitForRequest(WORKFLOW_RESET_API); await expect( - page.locator('#workflow-actions-menu-button').locator('visible=true'), + page.getByRole('button', { name: 'More Actions' }), ).toBeEnabled(); - await expect( - page - .locator('#workflow-actions-primary-button') - .locator('visible=true'), - ).toBeEnabled(); + await expect(page.getByRole('button', { name: 'Reset' })).toBeEnabled(); - await page - .locator('#workflow-actions-primary-button') - .locator('visible=true') - .click(); + await page.getByRole('button', { name: 'Reset' }).click(); await page .getByTestId('workflow-reset-event-id-select-button') @@ -223,19 +194,12 @@ test.describe('Workflow Actions for a Completed Workflow', () => { const requestPromise = page.waitForRequest(WORKFLOW_RESET_API); await expect( - page.locator('#workflow-actions-menu-button').locator('visible=true'), + page.getByRole('button', { name: 'More Actions' }), ).toBeEnabled(); - await expect( - page - .locator('#workflow-actions-primary-button') - .locator('visible=true'), - ).toBeEnabled(); + await expect(page.getByRole('button', { name: 'Reset' })).toBeEnabled(); - await page - .locator('#workflow-actions-primary-button') - .locator('visible=true') - .click(); + await page.getByRole('button', { name: 'Reset' }).click(); await page .getByTestId('workflow-reset-event-id-select-button') @@ -274,19 +238,12 @@ test.describe('Workflow Actions for a Completed Workflow', () => { const requestPromise = page.waitForRequest(WORKFLOW_RESET_API); await expect( - page.locator('#workflow-actions-menu-button').locator('visible=true'), + page.getByRole('button', { name: 'More Actions' }), ).toBeEnabled(); - await expect( - page - .locator('#workflow-actions-primary-button') - .locator('visible=true'), - ).toBeEnabled(); + await expect(page.getByRole('button', { name: 'Reset' })).toBeEnabled(); - await page - .locator('#workflow-actions-primary-button') - .locator('visible=true') - .click(); + await page.getByRole('button', { name: 'Reset' }).click(); await page .getByTestId('workflow-reset-event-id-select-button') @@ -334,21 +291,14 @@ test.describe('Workflow Actions for a Completed Workflow', () => { const requestPromise = page.waitForRequest(WORKFLOW_TERMINATE_API); await expect( - page.locator('#workflow-actions-menu-button').locator('visible=true'), + page.getByRole('button', { name: 'More Actions' }), ).toBeEnabled(); await expect(page.getByText('Latest Execution')).toBeVisible(); - await expect( - page - .locator('#workflow-actions-primary-button') - .locator('visible=true'), - ).toBeEnabled(); + await expect(page.getByRole('button', { name: 'Reset' })).toBeEnabled(); - await page - .locator('#workflow-actions-menu-button') - .locator('visible=true') - .click(); + await page.getByRole('button', { name: 'More Actions' }).click(); await page .getByTestId('terminate-button') @@ -370,21 +320,14 @@ test.describe('Workflow Actions for a Completed Workflow', () => { await mockEventHistoryApi(page); await expect( - page.locator('#workflow-actions-menu-button').locator('visible=true'), + page.getByRole('button', { name: 'More Actions' }), ).toBeEnabled(); await expect(page.getByText('Latest Execution')).toBeHidden(); - await expect( - page - .locator('#workflow-actions-primary-button') - .locator('visible=true'), - ).toBeEnabled(); + await expect(page.getByRole('button', { name: 'Reset' })).toBeEnabled(); - await page - .locator('#workflow-actions-menu-button') - .locator('visible=true') - .click(); + await page.getByRole('button', { name: 'More Actions' }).click(); await expect(page.getByTestId('terminate-button')).toBeHidden(); }); @@ -412,11 +355,11 @@ test.describe('Workflow actions for a Running Workflow', () => { await mockSettingsApi(page, { DisableWriteActions: true }); await expect( - page.locator('#workflow-actions-menu-button').locator('visible=true'), + page.getByRole('button', { name: 'More Actions' }), ).toBeDisabled(); await expect( - page.locator('#workflow-actions-primary-button').locator('visible=true'), + page.getByRole('button', { name: 'Request Cancellation' }), ).toBeDisabled(); }); @@ -427,36 +370,20 @@ test.describe('Workflow actions for a Running Workflow', () => { await mockSettingsApi(page, { WorkflowCancelDisabled: true }); await expect( - page - .locator('#workflow-actions-primary-button') - .locator('visible=true'), + page.getByRole('button', { name: 'Request Cancellation' }), ).toBeDisabled(); - await page - .locator('#workflow-actions-menu-button') - .locator('visible=true') - .click(); + await page.getByRole('button', { name: 'More Actions' }).click(); await expect( - page - .locator('#workflow-actions-menu') - .locator('visible=true') - .getByText('Send a Signal'), + page.getByRole('menuitem', { name: 'Send a Signal' }), ).toBeEnabled(); await expect( - page - .locator('#workflow-actions-menu') - .locator('visible=true') - .getByText('Terminate'), + page.getByRole('menuitem', { name: 'Terminate' }), ).toBeEnabled(); - await expect( - page - .locator('#workflow-actions-menu') - .locator('visible=true') - .getByText('Reset'), - ).toBeEnabled(); + await expect(page.getByRole('menuitem', { name: 'Reset' })).toBeEnabled(); }); }); @@ -467,36 +394,23 @@ test.describe('Workflow actions for a Running Workflow', () => { await mockSettingsApi(page, { WorkflowTerminateDisabled: true }); await expect( - page - .locator('#workflow-actions-primary-button') - .locator('visible=true'), + page.getByRole('button', { name: 'Request Cancellation' }), ).toBeEnabled(); - await page - .locator('#workflow-actions-menu-button') - .locator('visible=true') - .click(); + await page.getByRole('button', { name: 'More Actions' }).click(); await expect( - page - .locator('#workflow-actions-menu') - .locator('visible=true') - .getByText('Send a Signal'), + page.getByRole('menuitem', { name: 'Send a Signal' }), ).toBeEnabled(); await expect( page - .locator('#workflow-actions-menu') + .locator('#workflow-actions') .locator('visible=true') .getByText('Terminate'), ).toBeDisabled(); - await expect( - page - .locator('#workflow-actions-menu') - .locator('visible=true') - .getByText('Reset'), - ).toBeEnabled(); + await expect(page.getByRole('menuitem', { name: 'Reset' })).toBeEnabled(); }); }); @@ -507,36 +421,23 @@ test.describe('Workflow actions for a Running Workflow', () => { await mockSettingsApi(page, { WorkflowSignalDisabled: true }); await expect( - page - .locator('#workflow-actions-primary-button') - .locator('visible=true'), + page.getByRole('button', { name: 'Request Cancellation' }), ).toBeEnabled(); - await page - .locator('#workflow-actions-menu-button') - .locator('visible=true') - .click(); + await page.getByRole('button', { name: 'More Actions' }).click(); await expect( page - .locator('#workflow-actions-menu') + .locator('#workflow-actions') .locator('visible=true') .getByText('Send a Signal'), ).toBeDisabled(); await expect( - page - .locator('#workflow-actions-menu') - .locator('visible=true') - .getByText('Terminate'), + page.getByRole('menuitem', { name: 'Terminate' }), ).toBeEnabled(); - await expect( - page - .locator('#workflow-actions-menu') - .locator('visible=true') - .getByText('Reset'), - ).toBeEnabled(); + await expect(page.getByRole('menuitem', { name: 'Reset' })).toBeEnabled(); }); }); });