Repository navigation
fix(web): pull request diff follows the file tree, with a hide-tests filter #26730
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,7 @@ import { | |
| CheckIcon, | ||
| ChevronDownIcon, | ||
| Columns2Icon, | ||
| FlaskConicalOffIcon, | ||
| FolderTreeIcon, | ||
| PilcrowIcon, | ||
| Rows3Icon, | ||
|
|
@@ -37,6 +38,8 @@ import { | |
| useDiffPanelStore, | ||
| } from "../diffPanelStore"; | ||
| import { useLocalStorage } from "../hooks/useLocalStorage"; | ||
| import { isTestChangedFile } from "@t3tools/shared/changedFileKind"; | ||
| import { useHideTestFiles } from "./diffs/useHideTestFiles"; | ||
| import { useRenderedAppearance } from "../hooks/useTheme"; | ||
| import { useStableCallback } from "../hooks/useStableCallback"; | ||
| import { | ||
|
|
@@ -218,6 +221,7 @@ export default function DiffPanel({ | |
| false, | ||
| Schema.Boolean, | ||
| ); | ||
| const [hideTestFiles, setHideTestFiles] = useHideTestFiles(); | ||
| const [baseRefQuery, setBaseRefQuery] = useState(""); | ||
| const [codeViewRevision, setCodeViewRevision] = useState(0); | ||
|
|
||
|
|
@@ -539,6 +543,16 @@ export default function DiffPanel({ | |
| const isRefreshingDiff = branchDiffPreview.isPending || areFilePatchesPending; | ||
| const renderableFileEntries = renderableFiles.map(getCachedFileEntry); | ||
| const diffFileKeys = renderableFileEntries.map((file) => file.fileKey); | ||
| const testFileCount = renderableFiles.filter((file) => | ||
| isTestChangedFile(resolveFileDiffPath(file)), | ||
| ).length; | ||
| // Hiding is display-only: lazy loading indexes into the full list. A requested file stays visible. | ||
| const isFileShown = (file: FileDiffMetadata) => { | ||
| const path = resolveFileDiffPath(file); | ||
| return !hideTestFiles || path === selectedFilePath || !isTestChangedFile(path); | ||
| }; | ||
| const shownFileEntries = renderableFileEntries.filter(({ fileDiff }) => isFileShown(fileDiff)); | ||
| const shownFileKeys = shownFileEntries.map((file) => file.fileKey); | ||
| const defaultCollapsedDiffFileKeys = settings.diffFilesCollapsed | ||
| ? new Set(diffFileKeys) | ||
| : EMPTY_COLLAPSED_DIFF_FILE_KEYS; | ||
|
|
@@ -561,7 +575,7 @@ export default function DiffPanel({ | |
| count={renderableFiles.length - settledFileCount} | ||
| /> | ||
| ) : null; | ||
| const codeViewFiles = renderableFileEntries | ||
| const codeViewFiles = shownFileEntries | ||
| .filter( | ||
| ({ fileDiff }) => | ||
| !lazySource || !readyFilePaths || readyFilePaths.has(resolveFileDiffPath(fileDiff)), | ||
|
|
@@ -574,10 +588,12 @@ export default function DiffPanel({ | |
| collapsed: | ||
| collapsedDiffFileKeys.has(fileKey) || fileDiff.cacheKey?.endsWith(":pending") === true, | ||
| })); | ||
| const pendingFiles = renderableFiles.filter((file) => file.cacheKey?.endsWith(":pending")); | ||
| const allDiffFilesCollapsed = areAllDiffFilesCollapsed(diffFileKeys, collapsedDiffFileKeys); | ||
| const pendingFiles = renderableFiles.filter( | ||
| (file) => file.cacheKey?.endsWith(":pending") && isFileShown(file), | ||
| ); | ||
| const allDiffFilesCollapsed = areAllDiffFilesCollapsed(shownFileKeys, collapsedDiffFileKeys); | ||
| const diffLineStat = sourceLineStat ?? getDiffLineStat(renderableFiles); | ||
| const fileTreeEntries = diffFileTreeEntries(renderableFiles); | ||
| const fileTreeEntries = diffFileTreeEntries(renderableFiles.filter(isFileShown)); | ||
| const codeViewReveal = useCodeViewFileReveal(); | ||
| const revealDiffFile = (filePath: string) => { | ||
| const index = renderableFileEntries.findIndex( | ||
|
|
@@ -661,7 +677,7 @@ export default function DiffPanel({ | |
|
|
||
| const toggleDiffFileCollapse = () => { | ||
| setCodeViewRevision((current) => current + 1); | ||
| updateCollapsedDiffFiles((current) => toggleAllDiffFiles(diffFileKeys, current)); | ||
| updateCollapsedDiffFiles((current) => toggleAllDiffFiles(shownFileKeys, current)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Medium When test files are hidden, toggling all visible files clears their saved collapse state, so hidden tests reappear expanded even if they were individually collapsed. 🚀 Reply "fix it for me" or copy this AI Prompt for your agent: |
||
| }; | ||
|
|
||
| const selectTurn = (runId: RunId) => { | ||
|
|
@@ -991,6 +1007,28 @@ export default function DiffPanel({ | |
| {diffIgnoreWhitespace ? "Show whitespace changes" : "Hide whitespace changes"} | ||
| </TooltipPopup> | ||
| </Tooltip> | ||
| {testFileCount > 0 && ( | ||
| <Tooltip> | ||
| <TooltipTrigger | ||
| render={ | ||
| <Toggle | ||
| aria-label="Hide test files" | ||
| variant="ghost" | ||
| size="sm" | ||
| pressed={hideTestFiles} | ||
| onPressedChange={(pressed) => setHideTestFiles(Boolean(pressed))} | ||
| /> | ||
| } | ||
| > | ||
| <FlaskConicalOffIcon className="size-3.5" /> | ||
| </TooltipTrigger> | ||
| <TooltipPopup side="top"> | ||
| {hideTestFiles | ||
| ? `Show ${testFileCount} hidden test ${testFileCount === 1 ? "file" : "files"}` | ||
| : "Hide test files"} | ||
| </TooltipPopup> | ||
| </Tooltip> | ||
| )} | ||
| {diffFileKeys.length > 0 && ( | ||
| <Tooltip> | ||
| <TooltipTrigger | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import * as Schema from "effect/Schema"; | ||
|
|
||
| import { useLocalStorage } from "~/hooks/useLocalStorage"; | ||
|
|
||
| const HIDE_TEST_FILES_STORAGE_KEY = "t3code.diffHideTestFiles"; | ||
|
|
||
| export function useHideTestFiles() { | ||
| return useLocalStorage(HIDE_TEST_FILES_STORAGE_KEY, false, Schema.Boolean); | ||
| } |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Medium
When 🚀 Reply "fix it for me" or copy this AI Prompt for your agent: |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ import { | |
| ChevronDownIcon, | ||
| ChevronRightIcon, | ||
| Columns2Icon, | ||
| FlaskConicalOffIcon, | ||
| FolderTreeIcon, | ||
| InfoIcon, | ||
| MessageSquareOffIcon, | ||
|
|
@@ -51,6 +52,7 @@ import { | |
| canEditPullRequestComment, | ||
| canResolvePullRequestThread, | ||
| } from "@t3tools/client-runtime/pull-request-editing"; | ||
| import { isTestChangedFile } from "@t3tools/shared/changedFileKind"; | ||
| import { orderDiffFiles } from "./pullRequestFileOrder.logic"; | ||
| import { | ||
| buildFileDiffRenderKey, | ||
|
|
@@ -80,7 +82,8 @@ import { useDiffWorkerPool } from "../DiffWorkerPoolProvider"; | |
| import { DiffCommentAnnotation } from "../diffs/DiffCommentAnnotation"; | ||
| import { DiffFileTree } from "../diffs/DiffFileTree"; | ||
| import { useCodeViewFileReveal } from "../diffs/useCodeViewFileReveal"; | ||
| import { diffFileTreeEntries } from "../diffs/diffFileTree.logic"; | ||
| import { useHideTestFiles } from "../diffs/useHideTestFiles"; | ||
| import { diffFileTreeEntries, orderDiffFilesLikeTree } from "../diffs/diffFileTree.logic"; | ||
| import { StyledDiffCodeView } from "../diffs/StyledDiffCodeView"; | ||
| import { Button } from "../ui/button"; | ||
| import { MorphIcon } from "~/components/MorphIcon"; | ||
|
|
@@ -307,6 +310,7 @@ function PullRequestCodeTab({ | |
| false, | ||
| Schema.Boolean, | ||
| ); | ||
| const [hideTestFiles, setHideTestFiles] = useHideTestFiles(); | ||
| const [selectedLines, setSelectedLines] = useState<{ | ||
| id: string; | ||
| range: SelectedLineRange; | ||
|
|
@@ -496,9 +500,22 @@ function PullRequestCodeTab({ | |
| ignoreWhitespace, | ||
| ), | ||
| ); | ||
| const files = parsedSlices.flatMap((parsed) => | ||
| parsed?.kind === "files" ? orderDiffFiles(parsed.files) : [], | ||
| const loadedFiles = orderDiffFilesLikeTree( | ||
| parsedSlices.flatMap((parsed) => | ||
| parsed?.kind === "files" ? orderDiffFiles(parsed.files) : [], | ||
| ), | ||
| ); | ||
| const testFileCount = loadedFiles.filter((file) => | ||
| isTestChangedFile(resolveFileDiffPath(file)), | ||
| ).length; | ||
| // A requested file stays visible so a jump to a hidden test still lands. | ||
| const files = hideTestFiles | ||
| ? loadedFiles.filter((file) => { | ||
|
Comment on lines
+512
to
+513
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle the all-hidden result separately from an empty diff. If every loaded file is a test and pagination is complete, filtering produces no items. The existing empty-diff branch then says the pull request has no file changes and returns before rendering review conversations. Show an all-hidden state and retain access to those conversations.
|
||
| const path = resolveFileDiffPath(file); | ||
| return path === revealRequest?.path || !isTestChangedFile(path); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟠 High Enabling 🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Limit the reveal exception to the active request. After a guide opens a test file,
|
||
| }) | ||
| : loadedFiles; | ||
| const hiddenTestFileCount = loadedFiles.length - files.length; | ||
| const filePaths = files.map((file) => resolveFileDiffPath(file)); | ||
| const omittedFileStats = new Map( | ||
| loadedSlices.flatMap((slice) => | ||
|
|
@@ -1071,6 +1088,11 @@ function PullRequestCodeTab({ | |
| {files.length} {files.length === 1 ? "file" : "files"} | ||
| {nextCursor === null ? "" : "+"} | ||
| </span> | ||
| {hiddenTestFileCount > 0 ? ( | ||
| <Button type="button" size="xs" variant="link" onClick={() => setHideTestFiles(false)}> | ||
| {hiddenTestFileCount} test {hiddenTestFileCount === 1 ? "file" : "files"} hidden | ||
| </Button> | ||
| ) : null} | ||
| {filesViewed.enabled && files.length > 0 ? ( | ||
| <span className="flex min-w-0 items-center gap-1 tabular-nums"> | ||
| <span className="shrink-0"> | ||
|
|
@@ -1283,6 +1305,26 @@ function PullRequestCodeTab({ | |
| </TooltipPopup> | ||
| </Tooltip> | ||
| ) : null} | ||
| {testFileCount > 0 ? ( | ||
| <Tooltip> | ||
| <TooltipTrigger | ||
| render={ | ||
| <Toggle | ||
| aria-label="Hide test files" | ||
| variant="ghost" | ||
| size="sm" | ||
| pressed={hideTestFiles} | ||
| onPressedChange={(pressed) => setHideTestFiles(Boolean(pressed))} | ||
| /> | ||
| } | ||
| > | ||
| <FlaskConicalOffIcon className="size-3.5" /> | ||
| </TooltipTrigger> | ||
| <TooltipPopup side="top"> | ||
| {hideTestFiles ? "Show test files" : "Hide test files"} | ||
| </TooltipPopup> | ||
| </Tooltip> | ||
| ) : null} | ||
| {fileKeys.length > 0 ? ( | ||
| <Tooltip> | ||
| <TooltipTrigger | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Preserve an active test-file draft when filtering.
If the user is writing a comment on a test file that is not
selectedFilePath, enabling this filter removes the file.AnnotatableCodeView.tsxthen clears both the draft and its text because the file is absent fromfilesandpendingFiles. The unsent comment cannot be recovered. Keep the draft file visible or preserve its draft while it is hidden. (raw.githubusercontent.com)