From d2d61ba0c01d5addf051316dedc2b038cb2881e7 Mon Sep 17 00:00:00 2001
From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Date: Thu, 29 Oct 2026 15:04:43 -0700
Subject: [PATCH] Center plain Project table cells and label the linked PR
overflow (#34532)
* fix(github-project): make linked PR numbers clickable
* fix: open linked pull requests at their exact URLs
* test: assert complete project relation cell text
* fix: clarify linked pull request controls and overflow
* Fix linked pull request focus clipping and overflow label
* Keep linked pull request overflow outside clipped values
* fix(github-project): center plain project cells and say what +N means
Drop the clickable linked PR numbers: the numbers stay plain text as
before. Plain text cells (repository, parent issue, linked pull requests,
sub-issue progress) now center vertically like the other cells, and the
linked PR overflow reads "+N more" instead of a bare "+N".
* fix(github-project): keep the linked PR "+N more" visible when numbers truncate
---
.../components/github-project/ProjectCell.tsx | 31 +++++++++++++------
src/renderer/src/i18n/locales/en.json | 3 +-
.../github/project-field-mutations.test.ts | 6 ++--
...ithub-project-request-coordination.test.ts | 2 +-
...thub-project-provider-columns.unit.test.ts | 17 ++++++----
5 files changed, 38 insertions(+), 21 deletions(-)
diff --git a/src/renderer/src/components/github-project/ProjectCell.tsx b/src/renderer/src/components/github-project/ProjectCell.tsx
index e7e3f59589a..ade697abcf6 100644
--- a/src/renderer/src/components/github-project/ProjectCell.tsx
+++ b/src/renderer/src/components/github-project/ProjectCell.tsx
@@ -83,12 +83,14 @@ export default function ProjectCell({
}
if (field.dataType === 'REPOSITORY') {
return (
- {row.content.repository ?? ''}
+
+ {row.content.repository ?? ''}
+
)
}
if (field.dataType === 'PARENT_ISSUE') {
return (
-
+
{row.content.parentIssue ? `#${row.content.parentIssue.number}` : ''}
)
@@ -99,14 +101,23 @@ export default function ProjectCell({
return
}
const labels = linked.pullRequests.map((pr) => `#${pr.number}`)
- const overflow =
- linked.totalCount > linked.pullRequests.length
- ? ` +${linked.totalCount - linked.pullRequests.length}`
- : ''
+ const overflow = linked.totalCount - linked.pullRequests.length
+ // Only the numbers truncate, so "+N more" stays visible in narrow columns.
return (
-
- {labels.join(', ')}
- {overflow}
+
+ {labels.join(', ')}
+ {overflow > 0 ? (
+ <>
+ {' '}
+
+ {translate(
+ 'auto.components.github.project.ProjectCell.linkedPullRequestsOverflow',
+ '+{{count}} more',
+ { count: overflow }
+ )}
+
+ >
+ ) : null}
)
}
@@ -116,7 +127,7 @@ export default function ProjectCell({
return
}
return (
-
+
{progress.completed}/{progress.total} ({progress.percentCompleted}%)
)
diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json
index d0032297851..2b12409b1bb 100644
--- a/src/renderer/src/i18n/locales/en.json
+++ b/src/renderer/src/i18n/locales/en.json
@@ -3364,7 +3364,8 @@
"9cb1a0c984": "Add text",
"2e26a06c70": "Add label",
"36341ffc66": "Assign",
- "e369bf4fec": "Select"
+ "e369bf4fec": "Select",
+ "linkedPullRequestsOverflow": "+{{count}} more"
},
"ProjectGroupHeader": {
"82a22d2079": "Current",
diff --git a/src/renderer/src/store/github/project-field-mutations.test.ts b/src/renderer/src/store/github/project-field-mutations.test.ts
index 5fcbb47b59f..0fedd98b403 100644
--- a/src/renderer/src/store/github/project-field-mutations.test.ts
+++ b/src/renderer/src/store/github/project-field-mutations.test.ts
@@ -128,17 +128,17 @@ describe('project field writes', () => {
.getState()
.updateProjectFieldValue(key, 'row', 'status', { kind: 'single-select', optionId: 'done' })
expect(field(store)).toMatchObject({ optionId: 'done', name: 'Done', color: 'PURPLE' })
- expect(visibleRelations()).toEqual(['#12 +2', '1/2 (50%)'])
+ expect(visibleRelations()).toEqual(['#12 +2 more', '1/2 (50%)'])
const clear = store.getState().clearProjectFieldValue(key, 'row', 'status')
expect(field(store)).toBeUndefined()
- expect(visibleRelations()).toEqual(['#12 +2', '1/2 (50%)'])
+ expect(visibleRelations()).toEqual(['#12 +2 more', '1/2 (50%)'])
await vi.waitFor(() => expect(mockApi.gh.updateProjectItemField).toHaveBeenCalledTimes(1))
expect(mockApi.gh.clearProjectItemField).not.toHaveBeenCalled()
first.resolve({ ok: true })
await Promise.all([update, clear])
expect(mockApi.gh.clearProjectItemField).toHaveBeenCalledTimes(1)
expect(field(store)).toBeUndefined()
- expect(visibleRelations()).toEqual(['#12 +2', '1/2 (50%)'])
+ expect(visibleRelations()).toEqual(['#12 +2 more', '1/2 (50%)'])
})
it.each([true, false])(
diff --git a/src/renderer/src/store/slices/github-project-request-coordination.test.ts b/src/renderer/src/store/slices/github-project-request-coordination.test.ts
index 72cb784d6f4..5423e251553 100644
--- a/src/renderer/src/store/slices/github-project-request-coordination.test.ts
+++ b/src/renderer/src/store/slices/github-project-request-coordination.test.ts
@@ -101,7 +101,7 @@ describe('createGitHubSlice.fetchProjectViewTable coordination', () => {
expect(groups.map((group) => group.label)).toEqual(['1', '2'])
expect(groups.flatMap((group) => group.rows.map(projectRelationCellText))).toEqual([
['', ''],
- ['#12 +2', '1/2 (50%)']
+ ['#12 +2 more', '1/2 (50%)']
])
expect(cached.rows[1].content).not.toHaveProperty('linkedPullRequests')
expect(cached.rows[1].content).not.toHaveProperty('subIssuesProgress')
diff --git a/tests/e2e/github-project-provider-columns.unit.test.ts b/tests/e2e/github-project-provider-columns.unit.test.ts
index 0d382fdd9c6..76bc2eb0cfe 100644
--- a/tests/e2e/github-project-provider-columns.unit.test.ts
+++ b/tests/e2e/github-project-provider-columns.unit.test.ts
@@ -70,7 +70,11 @@ const args = {
unsupportedFields: new Set(),
host: 'acme.ghe.com'
} as const
-const prText = '#1, #2, #3, #4, #5, #6, #7, #8, #9, #10 +2'
+const prText = '#1, #2, #3, #4, #5, #6, #7, #8, #9, #10 +2 more'
+// The linked PR overflow renders in its own span, so match the element holding the whole cell text.
+const cellText = (text: string) => (_: string, element: Element | null) =>
+ element?.textContent === text &&
+ !Array.from(element.children).some((child) => child.textContent === text)
const missingSummary = {
extensions: { code: 'undefinedField', typeName: 'Issue', fieldName: 'subIssuesSummary' },
message: "Field 'subIssuesSummary' doesn't exist on type 'Issue'"
@@ -188,7 +192,7 @@ describe('Project columns from the items response', () => {
}
const row = normalized(page.page.nodes[0])
renderColumns(row)
- expect(screen.getByText(expectedText).textContent).toBe(expectedText)
+ expect(screen.getByText(cellText(expectedText)).textContent).toBe(expectedText)
expect(row.fieldValuesByFieldId).toEqual({
notes: { kind: 'text', fieldId: 'notes', text: 'Keep this too' }
})
@@ -253,7 +257,7 @@ describe('Project columns from the items response', () => {
expect(warning).toHaveBeenCalledTimes(parentDropped ? 1 : 0)
renderColumns(result.rows[0])
expect(Boolean(screen.queryByText('1/2 (50%)'))).toBe(summary)
- expect(Boolean(screen.queryByText(prText))).toBe(prs)
+ expect(Boolean(screen.queryByText(cellText(prText)))).toBe(prs)
expect(ghExecFileAsync).toHaveBeenCalledTimes(2)
const query = vi.mocked(ghExecFileAsync).mock.calls[1][0].join(' ')
expect(query.includes('parent {')).toBe(!parentDropped)
@@ -397,7 +401,8 @@ describe('Project columns from the items response', () => {
row.content.linkedPullRequests.totalCount = totalCount
renderColumns(row)
expect(
- screen.getByText(totalCount === 12 ? prText : prText.replace(' +2', '')).textContent
+ screen.getByText(cellText(totalCount === 12 ? prText : prText.replace(' +2 more', '')))
+ .textContent
).toBeTruthy()
})
@@ -453,7 +458,7 @@ describe('Project columns from the items response', () => {
const groups = groupRows(table, sorted)
expect(groups.map((group) => group.label)).toEqual(['1', '2'])
renderColumns(groups[1].rows[0])
- expect(screen.getByText(prText)).toBeTruthy()
+ expect(screen.getByText(cellText(prText))).toBeTruthy()
expect(screen.getByText('1/2 (50%)')).toBeTruthy()
cleanup()
renderColumns(groups[0].rows[0])
@@ -519,7 +524,7 @@ describe('Project columns from the items response', () => {
expect(arguments_.join(' ')).toContain('pullRequests(first:10)')
}
renderColumns(result.rows[count - 1])
- expect(screen.getByText(prText)).toBeTruthy()
+ expect(screen.getByText(cellText(prText))).toBeTruthy()
}
)
})