fix: hide project activity summary when idle

This commit is contained in:
mesalogo
2026-09-14 09:06:36 +08:00
parent 81ae9e0324
commit de14a6157a
8 changed files with 27 additions and 23 deletions
+4 -5
View File
@@ -1285,9 +1285,8 @@ describe("App", () => {
vi.mocked(api.tasks.list).mockResolvedValue([{ ...task, status: "completed" }]);
act(() => conversationQueueChangeListener?.(task.conversationId!));
await waitFor(() => expect(summary).toHaveTextContent("暂无活动"));
expect(screen.getByRole("menu", { name: "全项目活动" })).toHaveTextContent("暂无运行中或待处理会话");
fireEvent.keyDown(document.activeElement!, { key: "Escape" });
await waitFor(() => expect(summary).not.toBeInTheDocument());
expect(screen.queryByRole("menu", { name: "全项目活动" })).not.toBeInTheDocument();
fireEvent.click(screen.getByRole("button", { name: "当前项目" }));
expect(within(screen.getByRole("menu")).getByRole("menuitemradio", { name: /Background project/u }))
.not.toHaveTextContent("个运行中");
@@ -1517,8 +1516,8 @@ describe("App", () => {
: { requestId: request.requestId, type: "error", status: "failed", message: "Activity execution failed" });
conversationQueueChangeListener?.(request.conversationId!);
});
await waitFor(() => expect(screen.getByRole("button", { name: /全项目活动/u })).toHaveTextContent("暂无活动"));
expect(menu).toHaveTextContent("暂无运行中或待处理会话");
await waitFor(() => expect(screen.queryByRole("button", { name: /全项目活动/u })).not.toBeInTheDocument());
expect(menu).not.toBeInTheDocument();
});
});
+5 -8
View File
@@ -37,13 +37,10 @@ function open(): HTMLButtonElement {
afterEach(() => { cleanup(); vi.restoreAllMocks() })
describe('ProjectActivity', () => {
it('keeps an idle summary and an accessible empty menu while hiding zero counts', () => {
it('hides the idle summary and zero counts', () => {
render(view([]))
const trigger = open()
expect(trigger).toHaveTextContent('No activity')
expect(screen.getByRole('menu')).toHaveTextContent('No running conversations')
fireEvent.keyDown(document.activeElement!, { key: 'Escape' })
expect(trigger).toHaveFocus()
expect(screen.queryByRole('button', { name: /All project activity/ })).not.toBeInTheDocument()
expect(screen.queryByRole('menu')).not.toBeInTheDocument()
const { container } = render(<I18nextProvider i18n={i18n}><ProjectActivityCounts attention={0} running={0} /></I18nextProvider>)
expect(container).toBeEmptyDOMElement()
})
@@ -165,10 +162,10 @@ describe('ProjectActivity', () => {
rerender(view(activities.filter((activity) => activity.projectId === 'local')))
expect(screen.getByRole('menuitem', { name: /Local/ })).toHaveFocus()
rerender(view([]))
expect(screen.getByRole('menu')).toHaveTextContent('No running conversations')
fireEvent.keyDown(document.activeElement!, { key: 'Escape' })
expect(screen.queryByRole('button', { name: /All project activity/ })).not.toBeInTheDocument()
expect(screen.queryByRole('menu')).not.toBeInTheDocument()
rerender(view())
expect(screen.getByRole('button', { name: /All project activity/ })).toHaveAttribute('aria-expanded', 'false')
expect(screen.queryByRole('menu')).not.toBeInTheDocument()
})
+4 -3
View File
@@ -227,15 +227,16 @@ export function ProjectActivity({
const [open, setOpen] = useState(false)
const triggerRef = useRef<HTMLButtonElement>(null)
const id = useId()
if (!visible && open) setOpen(false)
if ((!visible || activities.length === 0) && open) setOpen(false)
const running = useMemo(() => activities.filter((activity) => activity.status === 'running').length, [activities])
if (activities.length === 0) return null
return (
<>
<button
aria-expanded={open}
aria-haspopup="menu"
aria-controls={open ? id : undefined}
aria-label={`${t('projectActivity.title')}: ${activities.length ? [t('projectActivity.attentionCount', { count: activities.length - running }), t('projectActivity.runningCount', { count: running })].join(', ') : t('projectActivity.idle')}`}
aria-label={`${t('projectActivity.title')}: ${[t('projectActivity.attentionCount', { count: activities.length - running }), t('projectActivity.runningCount', { count: running })].join(', ')}`}
ref={triggerRef}
className="project-activity__summary"
onClick={(event) => {
@@ -250,7 +251,7 @@ export function ProjectActivity({
}}
type="button"
>
{activities.length ? <ProjectActivityCounts attention={activities.length - running} running={running} /> : <span>{t('projectActivity.idle')}</span>}
<ProjectActivityCounts attention={activities.length - running} running={running} />
<ChevronDown aria-hidden="true" size={14} />
</button>
{open && visible && (