From 5faf11f4eb01b709f170b09874e5a9d09b22ac86 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Fri, 23 Oct 2026 01:00:17 -0700 Subject: [PATCH 1/4] Fold inactive tool grants on Connectors page Co-authored-by: flint Agent-Model: codex/gpt-6-sol/medium --- .../services/installable/toolInstallables.ts | 2 +- .../evidence/task-323-tools-fold-1440.png | Bin 0 -> 35142 bytes .../evidence/task-323-tools-fold-390.png | Bin 0 -> 32749 bytes .../__tests__/V2ConnectorTools.fold.test.tsx | 58 ++++++++++++++++++ .../v2/__tests__/V2ConnectorTools.test.tsx | 13 +++- .../v2/__tests__/v2-layout-invariants.test.ts | 11 ++++ .../src/v2/components/V2ConnectorTools.tsx | 52 ++++++---------- 7 files changed, 100 insertions(+), 36 deletions(-) create mode 100644 docs/design/evidence/task-323-tools-fold-1440.png create mode 100644 docs/design/evidence/task-323-tools-fold-390.png create mode 100644 frontend/src/v2/__tests__/V2ConnectorTools.fold.test.tsx diff --git a/backend/services/installable/toolInstallables.ts b/backend/services/installable/toolInstallables.ts index e7421b24f3..11babe5b22 100644 --- a/backend/services/installable/toolInstallables.ts +++ b/backend/services/installable/toolInstallables.ts @@ -131,7 +131,7 @@ const githubToolNames = (): string[] => toolDefinitions() export const buildGithubToolInstallable = () => ({ installableId: 'github', name: 'GitHub', - description: 'Issues and pull requests in this instance\'s own GitHub repository. Connecting your own GitHub is coming.', + description: 'Issues and pull requests in this instance\'s own GitHub repository.', version: '1.0.0', kind: 'app', source: 'builtin', diff --git a/docs/design/evidence/task-323-tools-fold-1440.png b/docs/design/evidence/task-323-tools-fold-1440.png new file mode 100644 index 0000000000000000000000000000000000000000..c50903ba010f4aa65dbf80f83f9d0c81e418787e GIT binary patch literal 35142 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 literal 0 HcmV?d00001 diff --git a/docs/design/evidence/task-323-tools-fold-390.png b/docs/design/evidence/task-323-tools-fold-390.png new file mode 100644 index 0000000000000000000000000000000000000000..308914a9893be7cc995176b9f87834100334367f GIT binary patch literal 32749 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 P@ { + const mock = { + get: jest.fn(), post: jest.fn(), patch: jest.fn(), delete: jest.fn(), + defaults: { baseURL: '', headers: { common: {} } }, + interceptors: { request: { use: jest.fn(), eject: jest.fn() }, response: { use: jest.fn(), eject: jest.fn() } }, + }; + return { __esModule: true, default: mock, ...mock }; +}); +const axios = jest.requireMock('axios').default; + +const auth = { + currentUser: { _id: 'u1', username: 'sam', email: 'sam@example.test' }, user: { _id: 'u1', username: 'sam', email: 'sam@example.test' }, token: 'user-jwt', + loading: false, error: null, isAuthenticated: true, register: jest.fn(), login: jest.fn(), signInWithToken: jest.fn(), logout: jest.fn(), updateProfile: jest.fn(), +}; +const tomorrow = new Date(Date.now() + 86_400_000).toISOString(); +const yesterday = new Date(Date.now() - 86_400_000).toISOString(); +const grant = (id: string, revokedAt: string | null) => ({ + grantId: id, installationId: 'inst-github', target: { kind: 'pod', id: 'p1' }, tools: ['github.list_issues'], + writeMode: 'read', budget: null, effectiveAudience: [], expiresAt: tomorrow, + revokedAt, revokedBy: revokedAt ? 'u1' : null, parentGrantId: null, rootGrantId: null, + createdAt: yesterday, grantedBy: 'u1', +}); +const catalog = [{ installableId: 'github', list: 'tools', label: 'GitHub', description: 'Issues', available: true, + tools: [{ name: 'github.list_issues', requiredWriteMode: 'read', irreversible: false }], connections: [] }]; + +beforeAll(async () => { await i18nReady; await i18n.changeLanguage('en'); }); + +test('two live grants stay visible and nine revoked grants start in one closed fold', async () => { + const grants = [grant('live-1', null), grant('live-2', null), + ...Array.from({ length: 9 }, (_, i) => grant(`revoked-${i}`, yesterday))]; + axios.get.mockImplementation((url: string) => { + if (url === '/api/installables') return Promise.resolve({ data: { installables: catalog } }); + if (url === '/api/pods/p1/grants') return Promise.resolve({ data: { grants } }); + if (url === '/api/registry/pods/p1/agents') return Promise.resolve({ data: { agents: [] } }); + if (url.includes('/calls')) return Promise.resolve({ data: { calls: [], counts: { total: 0, ok: 0, refused: 0, pending_approval: 0, failed: 0 } } }); + return Promise.reject(new Error(`unmocked ${url}`)); + }); + + const { container } = render( + + ); + + const fold = await screen.findByRole('button', { name: 'inactive · 9' }); + expect(fold).toHaveAttribute('aria-expanded', 'false'); + expect(container.querySelectorAll('.v2-connector-row')).toHaveLength(2); + expect(screen.queryByText(/not enabled|not yet|coming/i)).not.toBeInTheDocument(); + + fireEvent.click(fold); + await waitFor(() => expect(container.querySelectorAll('.v2-connector-row')).toHaveLength(11)); + expect(fold).toHaveAttribute('aria-expanded', 'true'); +}); diff --git a/frontend/src/v2/__tests__/V2ConnectorTools.test.tsx b/frontend/src/v2/__tests__/V2ConnectorTools.test.tsx index 7bde0b5efd..6d75cf05da 100644 --- a/frontend/src/v2/__tests__/V2ConnectorTools.test.tsx +++ b/frontend/src/v2/__tests__/V2ConnectorTools.test.tsx @@ -9,6 +9,13 @@ import V2ConnectorTools from '../components/V2ConnectorTools'; import { AuthContext } from '../../context/AuthContext'; import i18n, { i18nReady } from '../../i18n'; +// These existing cases exercise grant actions and row copy with the roster +// expanded. The collapsed interaction is covered by V2ConnectorTools.fold. +jest.mock('../components/V2InactiveFold', () => ({ + __esModule: true, + default: ({ children }) => <>{children}, +})); + jest.mock('axios', () => { const mock = { get: jest.fn(), post: jest.fn(), patch: jest.fn(), delete: jest.fn(), @@ -538,8 +545,8 @@ test('an unavailable not-yet tool row offers the same Ask affordance as unavaila return Promise.reject(new Error(`unmocked ${url}`)); }); renderTools(); - expect(await screen.findByText('not enabled on this instance · ask your operator')).toBeInTheDocument(); - const ask = screen.getByRole('link', { name: 'Ask' }); + const ask = await screen.findByRole('link', { name: 'Ask' }); + expect(screen.queryByText('not enabled on this instance · ask your operator')).not.toBeInTheDocument(); expect(ask).toHaveAttribute('href', 'https://github.com/Team-Commonly/commonly/issues/new?title=Connector%20request'); expect(ask.closest('.v2-connector-row')).toHaveClass('v2-connector-row--not-enabled'); }); @@ -676,7 +683,7 @@ test('Linear exposes only its pinned read mode in the not-yet row and Add form', expect(within(modeGroup).getByRole('button', { name: 'read' })).toHaveAttribute('aria-pressed', 'true'); }); -test('search filters by tool name and the segment hides not-yet rows; with no grants and no catalogue it renders nothing', async () => { +test('search filters by tool name; with no grants and no catalogue it renders nothing', async () => { renderTools(); await screen.findByRole('heading', { name: 'Tools' }); fireEvent.change(screen.getByRole('searchbox', { name: 'Search tools' }), { target: { value: 'comment' } }); diff --git a/frontend/src/v2/__tests__/v2-layout-invariants.test.ts b/frontend/src/v2/__tests__/v2-layout-invariants.test.ts index d34dda8a8d..7ffcbcbe5c 100644 --- a/frontend/src/v2/__tests__/v2-layout-invariants.test.ts +++ b/frontend/src/v2/__tests__/v2-layout-invariants.test.ts @@ -4605,3 +4605,14 @@ describe('BYO command boxes wrap at spaces', () => { expect(rule).not.toContain('word-break: break-all'); }); }); + +describe('connector inactive fold layout', () => { + const v2 = read('../v2.css'); + test('the toggle stays a full-width row with a visible keyboard focus state', () => { + const rule = ruleBody(v2, '.v2-root button.v2-connectors__inactive-toggle'); + expect(rule).toContain('width: 100%'); + expect(rule).toContain('min-height: 44px'); + expect(ruleBody(v2, '.v2-root button.v2-connectors__inactive-toggle:focus-visible')) + .toContain('box-shadow: var(--v2-focus-ring)'); + }); +}); diff --git a/frontend/src/v2/components/V2ConnectorTools.tsx b/frontend/src/v2/components/V2ConnectorTools.tsx index fbf75c98ed..135cd10693 100644 --- a/frontend/src/v2/components/V2ConnectorTools.tsx +++ b/frontend/src/v2/components/V2ConnectorTools.tsx @@ -20,6 +20,7 @@ import { V2Pod } from '../hooks/useV2Pods'; import { PlatformGlyph } from '../icons/platforms'; import { ActGlyph } from '../icons/glyphs'; import { hostedMcpErrorNotice } from './hostedMcpErrorNotice'; +import V2InactiveFold from './V2InactiveFold'; export type GrantWriteMode = 'read' | 'write' | 'write-with-confirm'; @@ -137,7 +138,6 @@ const MODE_RANK: Record = { read: 0, 'write-with-confirm // JSX, because eslint's i18next/no-literal-string (jsx-only) reads an array literal in // a JSX expression as copy — and these are ids, not words (TASK-164). const WRITE_MODES: GrantWriteMode[] = ['read', 'write-with-confirm', 'write']; -const SEGMENTS = ['all', 'granted', 'not-yet'] as const; /** The age as a unit-suffixed number ("23d", "5m", "just now"), from the timestamp and keys. */ export const shortAge = (date: string | null | undefined, now: number, t: (key: string, options?: Record) => string): string => { @@ -226,7 +226,6 @@ const V2ConnectorTools: React.FC = ({ pods, manualRevokeNotice = null }) const [joinPolicyFixPodId, setJoinPolicyFixPodId] = useState(null); const [policyFixedPodId, setPolicyFixedPodId] = useState(null); const [query, setQuery] = useState(''); - const [segment, setSegment] = useState<'all' | 'granted' | 'not-yet'>('all'); // Grants and trail rows carry relative ages; they must advance while the // page sits open and be re-read when the tab comes back (TASK-131). const now = useRelativeNow(); @@ -400,19 +399,20 @@ const V2ConnectorTools: React.FC = ({ pods, manualRevokeNotice = null }) const q = query.trim().toLowerCase(); const rows = useMemo(() => (grants || []).filter((grant) => { - if (segment === 'not-yet') return false; if (!q) return true; return toolLabel(grant).toLowerCase().includes(q) || grant.tools.some((tool) => tool.toLowerCase().includes(q)); // eslint-disable-next-line react-hooks/exhaustive-deps - }), [grants, q, segment, catalog]); - // A tool is "not yet" while no live grant on it exists anywhere the person can see. - // `now` is a dep: a grant that expires while the page sits open has to move - // back under Not yet on the same tick that drops the header count, or the - // count and the list disagree until a reload. - const notYet = useMemo(() => (segment === 'granted' ? [] : catalog.filter((entry) => ( + }), [grants, q, catalog]); + // Catalogue entries without a live grant join the inactive fold. `now` is + // a dep so expiry moves a grant into the fold without a reload. + const notYet = useMemo(() => catalog.filter((entry) => ( !entryIsGranted(entry, grants || [], now) && (!q || entry.label.toLowerCase().includes(q) || entry.tools.some((tool) => tool.name.toLowerCase().includes(q))) - ))), [catalog, grants, now, q, segment]); + )), [catalog, grants, now, q]); + const liveRows = rows.filter((grant) => !isDead(grant, now)); + const inactiveRows = rows.filter((grant) => isDead(grant, now)); + const matchedHiddenConnections = hiddenConnections.filter((connection) => !q || connection.label.toLowerCase().includes(q)); + const inactiveCount = inactiveRows.length + notYet.length + matchedHiddenConnections.length; // Grants, not entries: a person may hold two live grants on one tool. const grantedCount = (grants || []).filter((grant) => !isDead(grant, now)).length; // Entries, from the same predicate the Not yet list filters on, so the header @@ -739,7 +739,7 @@ const V2ConnectorTools: React.FC = ({ pods, manualRevokeNotice = null }) {dead && entry && isGranter(grant) ? ( // Grant again is the granter's too (Wren 67920): the mint 403s anyone but the Connection's owner. - ) : ( @@ -780,13 +780,11 @@ const V2ConnectorTools: React.FC = ({ pods, manualRevokeNotice = null }) {entry.description} - {!entry.available - ? t('tools.notEnabled', { defaultValue: 'not enabled on this instance · ask your operator' }) - : entry.connections.length === 0 + {entry.available && (entry.connections.length === 0 ? (isHosted ? t('tools.connectOwn', { defaultValue: 'connect your own {{label}} account', label: entry.label }) : t('tools.noConnection', { defaultValue: 'install the GitHub App first · an admin does this once' })) - : entryModeCopy(entry)} + : entryModeCopy(entry))} {canConnectHosted && entry.connectNotice && ( {entry.connectNotice} @@ -1084,29 +1082,19 @@ const V2ConnectorTools: React.FC = ({ pods, manualRevokeNotice = null }) aria-label={t('tools.search', { defaultValue: 'Search tools' })} onChange={(event) => setQuery(event.target.value)} /> -
- {SEGMENTS.map((key) => ( - - ))} -
{error && !draft && !selected && !githubAppSetup && (

{error}

)} {removedHiddenNotice &&

{t('tools.removedReviewProvider', { defaultValue: 'Connection removed. Review Commonly access at {{label}}.', label: removedHiddenNotice.label })} {t('tools.openProviderSettings', { defaultValue: 'Open provider settings' })}

}
- {rows.map(renderRow)} - {notYet.map(renderNotYet)} - {segment !== 'not-yet' && hiddenConnections.filter((connection) => !query || connection.label.toLowerCase().includes(q)).map(renderHiddenConnection)} - {rows.length === 0 && notYet.length === 0 && hiddenConnections.length === 0 && ( + {liveRows.map(renderRow)} + + {inactiveRows.map(renderRow)} + {notYet.map(renderNotYet)} + {matchedHiddenConnections.map(renderHiddenConnection)} + + {liveRows.length === 0 && inactiveCount === 0 && (

{t('tools.nothingMatches', { defaultValue: 'Nothing matches.' })}

)}
From 2a6683b0628be2b288086281c8c130dc11773b1b Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Fri, 23 Oct 2026 01:03:02 -0700 Subject: [PATCH 2/4] Cover expired grants in inactive fold test Co-authored-by: flint Agent-Model: codex/gpt-6-sol/medium --- frontend/src/v2/__tests__/V2ConnectorTools.fold.test.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/v2/__tests__/V2ConnectorTools.fold.test.tsx b/frontend/src/v2/__tests__/V2ConnectorTools.fold.test.tsx index 3c2d6859cb..fb5d6508bd 100644 --- a/frontend/src/v2/__tests__/V2ConnectorTools.fold.test.tsx +++ b/frontend/src/v2/__tests__/V2ConnectorTools.fold.test.tsx @@ -32,9 +32,10 @@ const catalog = [{ installableId: 'github', list: 'tools', label: 'GitHub', desc beforeAll(async () => { await i18nReady; await i18n.changeLanguage('en'); }); -test('two live grants stay visible and nine revoked grants start in one closed fold', async () => { +test('two live grants stay visible and nine revoked or expired grants start in one closed fold', async () => { const grants = [grant('live-1', null), grant('live-2', null), - ...Array.from({ length: 9 }, (_, i) => grant(`revoked-${i}`, yesterday))]; + ...Array.from({ length: 4 }, (_, i) => grant(`revoked-${i}`, yesterday)), + ...Array.from({ length: 5 }, (_, i) => ({ ...grant(`expired-${i}`, null), expiresAt: yesterday }))]; axios.get.mockImplementation((url: string) => { if (url === '/api/installables') return Promise.resolve({ data: { installables: catalog } }); if (url === '/api/pods/p1/grants') return Promise.resolve({ data: { grants } }); From b296360f7244f4826c620100647de9adf97c22ce Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Fri, 23 Oct 2026 01:07:09 -0700 Subject: [PATCH 3/4] Open inactive fold in connector callback tests Co-authored-by: flint Agent-Model: codex/gpt-6-sol/medium --- frontend/src/v2/__tests__/V2ConnectorsPage.test.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/src/v2/__tests__/V2ConnectorsPage.test.tsx b/frontend/src/v2/__tests__/V2ConnectorsPage.test.tsx index 5f8a853f06..d568aa0136 100644 --- a/frontend/src/v2/__tests__/V2ConnectorsPage.test.tsx +++ b/frontend/src/v2/__tests__/V2ConnectorsPage.test.tsx @@ -738,6 +738,7 @@ describe('V2ConnectorsPage', () => { }], [hostedLinearCatalogEntry()]); renderPage(); + fireEvent.click(await screen.findByRole('button', { name: 'inactive · 1' })); const reviewLink = await screen.findByRole('link', { name: "Open Linear's connected apps" }); expect(reviewLink).toHaveAttribute('href', revokePage); expect(reviewLink).toHaveAttribute('target', '_blank'); @@ -766,6 +767,7 @@ describe('V2ConnectorsPage', () => { try { renderPage(); + fireEvent.click(await screen.findByRole('button', { name: '非活跃 · 1' })); const reviewLink = await screen.findByRole('link', { name: '打开 Linear 的已连接应用' }); expect(reviewLink).toHaveAttribute('href', revokePage); const notice = screen.getByRole('status'); @@ -791,6 +793,7 @@ describe('V2ConnectorsPage', () => { }], [hostedLinearCatalogEntry()]); renderPage(); + fireEvent.click(await screen.findByRole('button', { name: 'inactive · 1' })); const linearDescription = await screen.findByText('Read Linear issues and projects.'); expect(linearDescription.closest('.v2-connector-row')).toBeInTheDocument(); await waitFor(() => expect(window.location.search).toBe('')); From 1a725f5502ebcd86e77739687c0e97f0106a5163 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Fri, 23 Oct 2026 01:21:21 -0700 Subject: [PATCH 4/4] Keep fold focus visible and cover mixed search Co-authored-by: flint Agent-Model: codex/gpt-6-sol/medium --- .../__tests__/V2ConnectorTools.fold.test.tsx | 27 +++++++++++++++++++ .../v2/__tests__/v2-layout-invariants.test.ts | 2 +- frontend/src/v2/v2.css | 2 +- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/frontend/src/v2/__tests__/V2ConnectorTools.fold.test.tsx b/frontend/src/v2/__tests__/V2ConnectorTools.fold.test.tsx index fb5d6508bd..78064494b4 100644 --- a/frontend/src/v2/__tests__/V2ConnectorTools.fold.test.tsx +++ b/frontend/src/v2/__tests__/V2ConnectorTools.fold.test.tsx @@ -57,3 +57,30 @@ test('two live grants stay visible and nine revoked or expired grants start in o await waitFor(() => expect(container.querySelectorAll('.v2-connector-row')).toHaveLength(11)); expect(fold).toHaveAttribute('aria-expanded', 'true'); }); + +test('search counts only matching inactive grants, catalogue entries and connections', async () => { + const entries = [catalog[0], { ...catalog[0], installableId: 'linear', connectionType: 'hosted-mcp', entryId: 'linear', label: 'Linear', description: 'Linear issues', + tools: [{ name: 'linear.list_issues', requiredWriteMode: 'read', irreversible: false }] }]; + axios.get.mockImplementation((url: string) => { + if (url === '/api/installables') return Promise.resolve({ data: { installables: entries, hiddenToolConnections: [ + { connectionId: 'hidden-1', entryId: 'atlassian', label: 'Atlassian', status: 'inactive', revokePage: null }, + ] } }); + if (url === '/api/pods/p1/grants') return Promise.resolve({ data: { grants: [grant('live', null), grant('revoked', yesterday)] } }); + if (url === '/api/registry/pods/p1/agents') return Promise.resolve({ data: { agents: [] } }); + if (url.includes('/calls')) return Promise.resolve({ data: { calls: [], counts: { total: 0, ok: 0, refused: 0, pending_approval: 0, failed: 0 } } }); + return Promise.reject(new Error(`unmocked ${url}`)); + }); + + const { container } = render( + + ); + expect(await screen.findByRole('button', { name: 'inactive · 3' })).toBeInTheDocument(); + + fireEvent.change(screen.getByRole('searchbox', { name: 'Search tools' }), { target: { value: 'Linear' } }); + const fold = screen.getByRole('button', { name: 'inactive · 1' }); + fireEvent.click(fold); + expect(container.querySelectorAll('.v2-connector-row')).toHaveLength(1); + expect(container.querySelector('.v2-connector-row')).toHaveTextContent('Linear'); + expect(container).not.toHaveTextContent('Atlassian'); + expect(container.querySelector('.v2-connector-row__name')).toHaveTextContent('Linear'); +}); diff --git a/frontend/src/v2/__tests__/v2-layout-invariants.test.ts b/frontend/src/v2/__tests__/v2-layout-invariants.test.ts index 7ffcbcbe5c..c9bebcce6d 100644 --- a/frontend/src/v2/__tests__/v2-layout-invariants.test.ts +++ b/frontend/src/v2/__tests__/v2-layout-invariants.test.ts @@ -4613,6 +4613,6 @@ describe('connector inactive fold layout', () => { expect(rule).toContain('width: 100%'); expect(rule).toContain('min-height: 44px'); expect(ruleBody(v2, '.v2-root button.v2-connectors__inactive-toggle:focus-visible')) - .toContain('box-shadow: var(--v2-focus-ring)'); + .toContain('box-shadow: inset 0 0 0 2px var(--v2-accent)'); }); }); diff --git a/frontend/src/v2/v2.css b/frontend/src/v2/v2.css index 768501f123..7de4a06634 100644 --- a/frontend/src/v2/v2.css +++ b/frontend/src/v2/v2.css @@ -10509,7 +10509,7 @@ body.modern-ui.v2-canvas { .v2-connectors__inactive-fold { min-width: 0; } .v2-root button.v2-connectors__inactive-toggle { display: flex; align-items: center; gap: 8px; width: 100%; min-height: 44px; padding: 10px 18px; border: 0; background: var(--v2-surface); color: var(--v2-text-tertiary); font: 500 12px/16px var(--v2-font-mono); text-align: left; cursor: pointer; } .v2-root button.v2-connectors__inactive-toggle:hover { background: var(--v2-surface-hover); } -.v2-root button.v2-connectors__inactive-toggle:focus-visible { box-shadow: var(--v2-focus-ring); outline: none; } +.v2-root button.v2-connectors__inactive-toggle:focus-visible { box-shadow: inset 0 0 0 2px var(--v2-accent); outline: none; } .v2-connectors__inactive-caret { width: 7px; height: 7px; border-right: 1px solid currentColor; border-bottom: 1px solid currentColor; transform: rotate(-45deg); } .v2-root button.v2-connectors__inactive-toggle[aria-expanded="true"] .v2-connectors__inactive-caret { transform: rotate(45deg); } .v2-connectors__inactive-rows > .v2-connector-row:last-child { border-bottom: 0; }