fix(workflow): refold coalesced disclosure cycles
This commit is contained in:
@@ -82,6 +82,8 @@ describe.skipIf(MODE === 'record')('web e2e: durable workflow run in Chat', () =
|
|||||||
expect(await disclosures.nth(0).getAttribute('aria-expanded')).toBeNull()
|
expect(await disclosures.nth(0).getAttribute('aria-expanded')).toBeNull()
|
||||||
expect(await disclosures.nth(1).getAttribute('role')).toBeNull()
|
expect(await disclosures.nth(1).getAttribute('role')).toBeNull()
|
||||||
expect(await disclosures.nth(1).getAttribute('aria-expanded')).toBeNull()
|
expect(await disclosures.nth(1).getAttribute('aria-expanded')).toBeNull()
|
||||||
|
expect(await disclosures.nth(0).evaluate(element => getComputedStyle(element).cursor)).not.toBe('pointer')
|
||||||
|
expect(await disclosures.nth(1).evaluate(element => getComputedStyle(element).cursor)).not.toBe('pointer')
|
||||||
const member = page.getByRole('button', { name: /^Open Reply with exactly the word/ })
|
const member = page.getByRole('button', { name: /^Open Reply with exactly the word/ })
|
||||||
await member.waitFor({ timeout: 15_000 })
|
await member.waitFor({ timeout: 15_000 })
|
||||||
await member.focus()
|
await member.focus()
|
||||||
@@ -148,10 +150,12 @@ describe.skipIf(MODE === 'record')('web e2e: durable workflow run in Chat', () =
|
|||||||
const terminalWorkflow = page.getByRole('button', { name: /^snapshot-flow/ })
|
const terminalWorkflow = page.getByRole('button', { name: /^snapshot-flow/ })
|
||||||
await terminalWorkflow.waitFor()
|
await terminalWorkflow.waitFor()
|
||||||
expect(await terminalWorkflow.getAttribute('aria-expanded')).toBe('false')
|
expect(await terminalWorkflow.getAttribute('aria-expanded')).toBe('false')
|
||||||
|
expect(await terminalWorkflow.evaluate(element => getComputedStyle(element).cursor)).toBe('pointer')
|
||||||
await terminalWorkflow.click()
|
await terminalWorkflow.click()
|
||||||
const terminalPhase = page.getByRole('button', { name: /^Run/ })
|
const terminalPhase = page.getByRole('button', { name: /^Run/ })
|
||||||
await terminalPhase.waitFor()
|
await terminalPhase.waitFor()
|
||||||
expect(await terminalPhase.getAttribute('aria-expanded')).toBe('false')
|
expect(await terminalPhase.getAttribute('aria-expanded')).toBe('false')
|
||||||
|
expect(await terminalPhase.evaluate(element => getComputedStyle(element).cursor)).toBe('pointer')
|
||||||
await terminalPhase.click()
|
await terminalPhase.click()
|
||||||
await page.getByText(CHILD_PROMPT, { exact: false }).waitFor()
|
await page.getByText(CHILD_PROMPT, { exact: false }).waitFor()
|
||||||
await expect.poll(
|
await expect.poll(
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: var(--dsw-alias-bg-module-platform);
|
background: var(--dsw-alias-bg-module-platform);
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.runHeader:focus-visible {
|
.runHeader:focus-visible {
|
||||||
@@ -78,7 +77,6 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.phaseHeader:focus-visible {
|
.phaseHeader:focus-visible {
|
||||||
|
|||||||
@@ -84,10 +84,11 @@ function ManualDisclosure(props: StatusDisclosureProps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function StatusDisclosure({ requiresExpansion, ...props }: StatusDisclosureProps & {
|
function StatusDisclosure({ cleanCycleKey, requiresExpansion, ...props }: StatusDisclosureProps & {
|
||||||
|
readonly cleanCycleKey: number
|
||||||
readonly requiresExpansion: boolean
|
readonly requiresExpansion: boolean
|
||||||
}) {
|
}) {
|
||||||
if (!requiresExpansion) return <ManualDisclosure {...props} />
|
if (!requiresExpansion) return <ManualDisclosure key={cleanCycleKey} {...props} />
|
||||||
return <DisclosureRow {...props} open expandable={false} onToggle={forcedOpenToggle} />
|
return <DisclosureRow {...props} open expandable={false} onToggle={forcedOpenToggle} />
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,6 +139,7 @@ function RunHeader({ children, count, name, requiresExpansion, status, t }: {
|
|||||||
<StatusDisclosure
|
<StatusDisclosure
|
||||||
icon={<IconChevronRightOutline14 />}
|
icon={<IconChevronRightOutline14 />}
|
||||||
title={t('run.title', { name })}
|
title={t('run.title', { name })}
|
||||||
|
cleanCycleKey={count}
|
||||||
requiresExpansion={requiresExpansion}
|
requiresExpansion={requiresExpansion}
|
||||||
expandOnRowClick
|
expandOnRowClick
|
||||||
previewChevron={false}
|
previewChevron={false}
|
||||||
@@ -201,6 +203,7 @@ function PhaseSection({ phase, navigable, openSession, t }: {
|
|||||||
<StatusDisclosure
|
<StatusDisclosure
|
||||||
icon={<IconChevronRightOutline14 />}
|
icon={<IconChevronRightOutline14 />}
|
||||||
title={readablePhase(phase.phase, t)}
|
title={readablePhase(phase.phase, t)}
|
||||||
|
cleanCycleKey={phase.members.length}
|
||||||
requiresExpansion={phaseRequiresExpansion(phase)}
|
requiresExpansion={phaseRequiresExpansion(phase)}
|
||||||
expandOnRowClick
|
expandOnRowClick
|
||||||
previewChevron={false}
|
previewChevron={false}
|
||||||
|
|||||||
@@ -377,6 +377,45 @@ describe('WorkflowRunPanel', () => {
|
|||||||
expect(screen.queryByText('未分阶段')).toBeNull()
|
expect(screen.queryByText('未分阶段')).toBeNull()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('refolds when a complete activity cycle arrives as one clean update', () => {
|
||||||
|
const firstMember = {
|
||||||
|
seq: 1, label: 'first', childId: 'child-1' as SessionId, status: 'completed' as const,
|
||||||
|
}
|
||||||
|
const phaseClean: WorkflowRunChatData = {
|
||||||
|
name: 'phase-cycle', status: 'running',
|
||||||
|
phases: [phase({ members: [firstMember] })],
|
||||||
|
}
|
||||||
|
const phaseView = render(<WorkflowRunPanel {...panelProps(phaseClean)} />)
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: /未分阶段/ }))
|
||||||
|
expect(screen.getByText('first')).toBeTruthy()
|
||||||
|
phaseView.rerender(<WorkflowRunPanel {...panelProps({
|
||||||
|
...phaseClean,
|
||||||
|
phases: [phase({ members: [firstMember, {
|
||||||
|
seq: 2, label: 'second', childId: 'child-2' as SessionId, status: 'completed',
|
||||||
|
}] })],
|
||||||
|
})} />)
|
||||||
|
expect(screen.getByRole('button', { name: /未分阶段/ }).getAttribute('aria-expanded')).toBe('false')
|
||||||
|
expect(screen.queryByText('first')).toBeNull()
|
||||||
|
expect(screen.queryByText('second')).toBeNull()
|
||||||
|
phaseView.unmount()
|
||||||
|
|
||||||
|
const workflowClean: WorkflowRunChatData = {
|
||||||
|
name: 'workflow-cycle', status: 'completed',
|
||||||
|
phases: [phase({ members: [firstMember] })],
|
||||||
|
}
|
||||||
|
const workflowView = render(<WorkflowRunPanel {...panelProps(workflowClean)} />)
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: /^workflow-cycle/ }))
|
||||||
|
expect(screen.getByText('未分阶段')).toBeTruthy()
|
||||||
|
workflowView.rerender(<WorkflowRunPanel {...panelProps({
|
||||||
|
...workflowClean,
|
||||||
|
phases: [phase({ members: [firstMember, {
|
||||||
|
seq: 2, label: 'second', childId: 'child-2' as SessionId, status: 'completed',
|
||||||
|
}] })],
|
||||||
|
})} />)
|
||||||
|
expect(screen.getByRole('button', { name: /^workflow-cycle/ }).getAttribute('aria-expanded')).toBe('false')
|
||||||
|
expect(screen.queryByText('未分阶段')).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
it('derives the zero-member running and completed states from the current run status', () => {
|
it('derives the zero-member running and completed states from the current run status', () => {
|
||||||
const running: WorkflowRunChatData = { name: 'empty', status: 'running', phases: [] }
|
const running: WorkflowRunChatData = { name: 'empty', status: 'running', phases: [] }
|
||||||
const view = render(<WorkflowRunPanel {...panelProps(running)} />)
|
const view = render(<WorkflowRunPanel {...panelProps(running)} />)
|
||||||
|
|||||||
Reference in New Issue
Block a user