Skip to content

Commit f2dbcb3

Browse files
committed
fix(web-ui): align motion presence contracts
1 parent 16d7998 commit f2dbcb3

5 files changed

Lines changed: 53 additions & 29 deletions

File tree

‎src/web-ui/src/app/startup/startupPerformanceContract.test.ts‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -688,25 +688,25 @@ describe('startup performance contract', () => {
688688
expect(appLayoutSource).not.toMatch(/import\s+\{\s*NewProjectDialog\s*\}\s+from/);
689689
expect(appLayoutSource).toContain('const NewProjectDialog = lazy');
690690
expect(appLayoutSource).toContain("import('../components/NewProjectDialog')");
691-
expect(appLayoutSource).toContain('{showNewProjectDialog && (');
691+
expect(appLayoutSource).toContain('<PresenceBoundary active={showNewProjectDialog}>');
692692

693693
expect(workspaceItemSource).not.toMatch(/import\s+WorkspaceRelatedPathsDialog\s+from/);
694694
expect(workspaceItemSource).not.toMatch(/import\s+WorkspaceSessionBatchModal\s+from/);
695695
expect(workspaceItemSource).not.toMatch(/import\s+ScheduledJobsModal\s+from/);
696696
expect(workspaceItemSource).toContain("lazy(() => import('./WorkspaceRelatedPathsDialog'))");
697697
expect(workspaceItemSource).toContain("lazy(() => import('./WorkspaceSessionBatchModal'))");
698698
expect(workspaceItemSource).toContain("lazy(() => import('@/app/components/scheduled-jobs/ScheduledJobsModal'))");
699-
expect(workspaceItemSource).toContain('{relatedPathsDialogOpen && (');
700-
expect(workspaceItemSource).toContain('{sessionBatchModalOpen && (');
701-
expect(workspaceItemSource).toContain('{scheduledJobsModalOpen && (');
699+
expect(workspaceItemSource).toContain('<PresenceBoundary active={relatedPathsDialogOpen}>');
700+
expect(workspaceItemSource).toContain('<PresenceBoundary active={sessionBatchModalOpen}>');
701+
expect(workspaceItemSource).toContain('<PresenceBoundary active={scheduledJobsModalOpen}>');
702702

703703
expect(sessionsSectionSource).not.toMatch(/import\s+ScheduledJobsModal\s+from/);
704704
expect(sessionsSectionSource).toContain("lazy(() => import('@/app/components/scheduled-jobs/ScheduledJobsModal'))");
705-
expect(sessionsSectionSource).toContain('{scheduledJobsSession && (');
705+
expect(sessionsSectionSource).toContain('<PresenceBoundary active={scheduledJobsSession != null}>');
706706

707707
expect(footerActionsSource).not.toMatch(/import\s+\{\s*RemoteConnectDialog\s*\}\s+from/);
708708
expect(footerActionsSource).toContain("lazy(() => import('../../RemoteConnectDialog'))");
709-
expect(footerActionsSource).toContain('{showRemoteConnect && (');
709+
expect(footerActionsSource).toContain('<PresenceBoundary active={showRemoteConnect}>');
710710

711711
expect(newProjectDialogSource).not.toMatch(/from\s+['"]@tauri-apps\/plugin-dialog['"]/);
712712
expect(newProjectDialogSource).not.toContain("await import('@tauri-apps/plugin-dialog')");

‎src/web-ui/src/infrastructure/appearance/builtins/appearancePresetOutput.test.ts‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -149,42 +149,42 @@ describe('builtin appearance preset output', () => {
149149
}))).toMatchInlineSnapshot(`
150150
[
151151
{
152-
"hash": "b70ab1232690e55f3446d84c5c5d4459c508c6cd5f1e0bfe8565eba82e97ea6e",
152+
"hash": "954317380f5baa31e8b0155564c72606a76ff6fa6d8a190615120668a8e3d388",
153153
"id": "bitfun-light",
154154
"type": "light",
155155
},
156156
{
157-
"hash": "6c426dc8aacd8095fdf5a69ed80b4d42c0235cf72f61b20186fc14d5ebcb320b",
157+
"hash": "89abef6e2224b6fb86ac8bb34578c0d7ef725172ec1f29b29818038288b5dae0",
158158
"id": "bitfun-slate",
159159
"type": "dark",
160160
},
161161
{
162-
"hash": "db937836a31fb02d67f6035f1b3a036b8161f3833fdbe2634f37936ba7ef6357",
162+
"hash": "953efa0aa939f0080429972c3c0a7c46131523e106e8135cf543ec073d2e9d70",
163163
"id": "bitfun-dark",
164164
"type": "dark",
165165
},
166166
{
167-
"hash": "e1455a17d5891da49f584ac1da6740a04db152afd6cdd571a9406f86811e6c8b",
167+
"hash": "6b3d5817b0bf0568739ddfe2bbc5a7287d3db58761eda61379814f321195b7ed",
168168
"id": "bitfun-midnight",
169169
"type": "dark",
170170
},
171171
{
172-
"hash": "7944cb574fd2b30cd4ed766e1b25e86eabd4c90b4a0e054489231a68de5cc424",
172+
"hash": "e2dff5cddb2442779ca59ba92280cc3f01384a732c010f2e3dab8c399402e487",
173173
"id": "bitfun-china-style",
174174
"type": "light",
175175
},
176176
{
177-
"hash": "f66e855842633612ca3aed2c30ff8df057dd0768eeb47e89e049efab8968f227",
177+
"hash": "4fb76f4da97bd97213e5f9f1d35dc2e369cffb0d85a076541edbacbec1d85166",
178178
"id": "bitfun-china-night",
179179
"type": "dark",
180180
},
181181
{
182-
"hash": "b72278893e14c1b0cef86449facf7ba61b4f714f077c3923241ebb012a90e3c8",
182+
"hash": "a443ee9e0d5fd27e35aaf42fae2de7ba2ebd96841b9380794159ce2306c2c8c6",
183183
"id": "bitfun-cyber",
184184
"type": "dark",
185185
},
186186
{
187-
"hash": "abcd6c91fdc1c03ea66d7d460401351620d672130e4742cce6a8be7f04b22626",
187+
"hash": "e6b73a7c2569ffc251d0223f1bc57dc0c750b4ffbc9b1225ac8715b9a1f8fc5d",
188188
"id": "bitfun-tokyo-night",
189189
"type": "dark",
190190
},

‎src/web-ui/src/infrastructure/config/components/ExternalMcpOverview.test.tsx‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,11 @@ describe('ExternalMcpOverview', () => {
738738
record: {
739739
...snapshot.sources[0].record,
740740
health: 'degraded',
741-
diagnostics: [{ severity: 'warning', code: 'source_stale', message: 'details' }],
741+
diagnostics: [{
742+
severity: 'warning',
743+
code: 'source_stale',
744+
message: 'sensitive source diagnostic',
745+
}],
742746
},
743747
}],
744748
mcpServers: snapshot.mcpServers,
@@ -752,7 +756,7 @@ describe('ExternalMcpOverview', () => {
752756

753757
expect(container.textContent).toContain('external.status.degraded');
754758
expect(container.textContent).toContain('external.status.approvalRequired');
755-
expect(container.textContent).not.toContain('details');
759+
expect(container.textContent).not.toContain('sensitive source diagnostic');
756760
});
757761

758762
it('summarizes top-level MCP diagnostics without exposing their raw message', async () => {

‎src/web-ui/src/infrastructure/config/components/common/ConfigCollectionItem.test.tsx‎

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// @vitest-environment jsdom
22

33
import React, { act } from 'react';
4-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
4+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
55
import { createRoot, type Root } from 'react-dom/client';
66
import ConfigCollectionItem from './ConfigCollectionItem';
77

@@ -18,9 +18,11 @@ describe('ConfigCollectionItem', () => {
1818
afterEach(() => {
1919
act(() => root.unmount());
2020
container.remove();
21+
vi.useRealTimers();
2122
});
2223

2324
it('uses an independent native button for expandable details', () => {
25+
vi.useFakeTimers();
2426
act(() => {
2527
root.render(
2628
<ConfigCollectionItem
@@ -43,18 +45,29 @@ describe('ConfigCollectionItem', () => {
4345
act(() => {
4446
control?.click();
4547
});
46-
const details = container.querySelector<HTMLElement>('.bitfun-collection-item__details-collapse');
47-
expect(details?.dataset.open).toBe('false');
48-
expect(details?.getAttribute('aria-hidden')).toBe('true');
48+
expect(container.querySelector('.bitfun-collection-item__details-collapse')).toBeNull();
4949

5050
act(() => {
5151
toggle?.click();
5252
});
5353

54+
const details = container.querySelector<HTMLElement>('.bitfun-collection-item__details-collapse');
5455
expect(toggle?.getAttribute('aria-expanded')).toBe('true');
5556
expect(container.textContent).toContain('Configuration location');
5657
expect(details?.dataset.open).toBe('true');
5758
expect(details?.getAttribute('aria-hidden')).toBe('false');
59+
60+
act(() => {
61+
toggle?.click();
62+
});
63+
expect(details?.dataset.open).toBe('false');
64+
expect(details?.getAttribute('aria-hidden')).toBe('true');
65+
expect(details?.hasAttribute('inert')).toBe(true);
66+
67+
act(() => {
68+
vi.advanceTimersByTime(180);
69+
});
70+
expect(container.querySelector('.bitfun-collection-item__details-collapse')).toBeNull();
5871
});
5972

6073
it('does not expose disabled details as an interactive control', () => {
@@ -78,6 +91,6 @@ describe('ConfigCollectionItem', () => {
7891
});
7992

8093
expect(toggle?.getAttribute('aria-expanded')).toBe('false');
81-
expect(container.querySelector('.bitfun-collection-item__details-collapse')?.getAttribute('aria-hidden')).toBe('true');
94+
expect(container.querySelector('.bitfun-collection-item__details-collapse')).toBeNull();
8295
});
8396
});

‎src/web-ui/src/infrastructure/config/components/common/ConfigCollectionItem.tsx‎

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { useId, useState } from 'react';
22
import { ChevronDown } from 'lucide-react';
3+
import { PresenceBoundary } from '@/component-library';
34
import './ConfigCollectionItem.scss';
45

56
export interface ConfigCollectionItemProps extends React.HTMLAttributes<HTMLDivElement> {
@@ -91,15 +92,21 @@ export const ConfigCollectionItem: React.FC<ConfigCollectionItemProps> = ({
9192
</div>
9293

9394
{details ? (
94-
<div
95-
id={detailsId}
96-
className="bitfun-collection-item__details-collapse"
97-
data-open={isExpanded ? 'true' : 'false'}
98-
aria-hidden={!isExpanded}
99-
{...(!isExpanded ? { inert: '' } : {})}
95+
<PresenceBoundary
96+
active={isExpanded}
97+
exitDurationMs={180}
98+
minimumExitDurationMs={180}
10099
>
101-
<div className="bitfun-collection-item__details" data-bf-component="config" data-bf-part="collectionDetails">{details}</div>
102-
</div>
100+
<div
101+
id={detailsId}
102+
className="bitfun-collection-item__details-collapse"
103+
data-open={isExpanded ? 'true' : 'false'}
104+
aria-hidden={!isExpanded}
105+
{...(!isExpanded ? { inert: '' } : {})}
106+
>
107+
<div className="bitfun-collection-item__details" data-bf-component="config" data-bf-part="collectionDetails">{details}</div>
108+
</div>
109+
</PresenceBoundary>
103110
) : null}
104111
</div>
105112
);

0 commit comments

Comments
 (0)