feat(new-webui): Add keyboard scroll support to AntD virtual table used by search results and ingest jobs (resolves #944).#972
Conversation
WalkthroughA new Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant VirtualTable
participant AntdTable
User ->> VirtualTable: Keyboard event (Arrow/Page/Home/End)
VirtualTable ->> VirtualTable: handleKeyDown adjusts scroll position
User ->> VirtualTable: Renders with data, columns, etc.
VirtualTable ->> AntdTable: Forwards props, enables virtual scrolling
AntdTable -->> User: Renders virtualized table rows
Possibly related PRs
Suggested reviewers
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 4
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (4)
components/log-viewer-webui/client/src/components/VirtualTable/index.tsx(1 hunks)components/log-viewer-webui/client/src/components/VirtualTable/typings.tsx(1 hunks)components/log-viewer-webui/client/src/pages/IngestPage/Jobs/index.tsx(2 hunks)components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTable/index.tsx(2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}`: - Prefer `false == ` rather than `!`.
**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}: - Preferfalse == <expression>rather than!<expression>.
components/log-viewer-webui/client/src/components/VirtualTable/typings.tsxcomponents/log-viewer-webui/client/src/pages/IngestPage/Jobs/index.tsxcomponents/log-viewer-webui/client/src/components/VirtualTable/index.tsxcomponents/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTable/index.tsx
🧬 Code Graph Analysis (1)
components/log-viewer-webui/client/src/components/VirtualTable/index.tsx (1)
components/log-viewer-webui/client/src/components/VirtualTable/typings.tsx (3)
VirtualTableProps(17-17)VIRTUAL_TABLE_HOLDER_SELECTOR(12-12)SCROLL_INCREMENT(7-7)
🪛 Biome (1.9.4)
components/log-viewer-webui/client/src/components/VirtualTable/index.tsx
[error] 77-77: The HTML element div is non-interactive. Do not use tabIndex.
Adding non-interactive elements to the keyboard navigation flow can confuse users.
Unsafe fix: Remove the tabIndex attribute.
(lint/a11y/noNoninteractiveTabindex)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: lint-check (macos-latest)
- GitHub Check: lint-check (ubuntu-latest)
- GitHub Check: build (ubuntu-latest)
🔇 Additional comments (9)
components/log-viewer-webui/client/src/components/VirtualTable/typings.tsx (2)
17-17: Well-designed type definition.Good design choice to omit the "virtual" property since the VirtualTable component manages virtualization internally.
12-12:Details
❓ Verification inconclusive
CSS selector dependency on internal antd classes poses maintenance risk.
This selector targets antd's internal class structure which could break with library updates. Consider documenting this dependency and adding fallback logic.
🏁 Script executed:
#!/bin/bash # Description: Check if this CSS selector is documented in antd source or mentioned in other projects # Expected: Find usage patterns or documentation rg -A 3 -B 3 "ant-table-tbody-virtual-holder" --type typescript --type javascriptLength of output: 116
🏁 Script executed:
#!/bin/bash # Re-run search for the internal antd CSS class across all JS/TS files without relying on ripgrep’s --type filters rg -A 3 -B 3 "ant-table-tbody-virtual-holder" -g '*.ts' -g '*.tsx' -g '*.js' -g '*.jsx'Length of output: 825
Document and Mitigate Internal Antd CSS Dependency
The selector.ant-table-tbody-virtual-holderonly appears here and targets Ant Design’s internal DOM structure for virtual scrolling. Relying on it risks breakage when upgrading antd.• Document this dependency (e.g. in your component JSDoc or project README)
• Add fallback logic or feature-detection in case the class name changes
• Whenever you bump antd, verify that this selector still applies
• If possible, prefer a public API (e.g. custom row renderers or built-in virtual list components) over internal classescomponents/log-viewer-webui/client/src/pages/IngestPage/Jobs/index.tsx (1)
10-10: Good component replacement for virtual scrolling support.The switch to VirtualTable enables keyboard navigation for the jobs table, which improves accessibility.
components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTable/index.tsx (3)
7-7: Good import of the new VirtualTable component.This enables keyboard scrolling support for search results.
44-47: Container styling aligns with VirtualTable focus management.The outline: "none" style is appropriate since VirtualTable handles focus styling internally.
50-50: Explicit null handling improves robustness.Good defensive programming to explicitly handle the case where searchResults might be null or undefined.
components/log-viewer-webui/client/src/components/VirtualTable/index.tsx (3)
27-30: Proper null check follows coding guidelines.The use of
null === containerRef.currentcorrectly follows the coding guideline preference for explicit equality checks.
77-77: TabIndex usage is appropriate despite static analysis warning.The static analysis tool flagged tabIndex=0 on a div, but this is actually correct for keyboard navigation. The div needs to be focusable to receive keyboard events for table scrolling.
🧰 Tools
🪛 Biome (1.9.4)
[error] 77-77: The HTML element div is non-interactive. Do not use tabIndex.
Adding non-interactive elements to the keyboard navigation flow can confuse users.
Unsafe fix: Remove the tabIndex attribute.(lint/a11y/noNoninteractiveTabindex)
81-81:Details
✅ Verification successful
Verify virtual prop is always effective.
Ensure that virtual={true} always enables virtualization in antd tables, or add fallback handling if virtualization fails to activate.
🏁 Script executed:
#!/bin/bash # Description: Check antd documentation for virtual table requirements # Expected: Find conditions when virtual mode is enabled rg -A 10 -B 5 "virtual.*true" --type typescript --type javascriptLength of output: 102
🏁 Script executed:
#!/bin/bash # Inspect virtualization setup in the VirtualTable component # 1. Locate the VirtualTable file echo "File path:" rg -l "virtual={true}" -g "*.tsx" # 2. Show context around Table component usage echo -e "\nTable usage in VirtualTable:" rg -n "Table" -C3 components/log-viewer-webui/client/src/components/VirtualTable/index.tsx # 3. Show where virtual prop is applied echo -e "\nvirtual prop context:" rg -n "virtual" -C3 components/log-viewer-webui/client/src/components/VirtualTable/index.tsx # 4. Check scroll prop presence (required for antd virtualization) echo -e "\nscroll prop context:" rg -n "scroll" -C3 components/log-viewer-webui/client/src/components/VirtualTable/index.tsxLength of output: 3676
Virtual Table virtualization fallback already handled
The
virtual={true}prop on Ant Design’s Table always attempts to enable virtualization. In ourVirtualTablewrapper we guard against failures by checking for the virtualization container before applying keyboard-driven scrolling:• In
components/log-viewer-webui/client/src/components/VirtualTable/index.tsxaround lines 32–38, we do:const scrollNode = containerRef.current.querySelector<HTMLElement>( VIRTUAL_TABLE_HOLDER_SELECTOR ); if (scrollNode === null) { return; // fallback: skip virtualization if container not found }This early return ensures that, if virtualization doesn’t activate for any reason, no scroll operations are attempted. No further changes are needed here.
| /** | ||
| * Number of pixels to scroll vertically when using keyboard arrow navigation. | ||
| */ | ||
| export const SCROLL_INCREMENT = 32; |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Consider making the scroll increment configurable.
The fixed 32-pixel increment works well for most cases, but consider making this configurable through props for different use cases or accessibility needs.
🤖 Prompt for AI Agents
In components/log-viewer-webui/client/src/components/VirtualTable/typings.tsx at
line 7, the scroll increment is currently a fixed constant set to 32 pixels. To
make it configurable, refactor the code to accept the scroll increment value as
a prop in the relevant component(s). Replace the fixed SCROLL_INCREMENT usage
with this prop, providing a default value of 32 if no prop is supplied, allowing
different scroll increments for various use cases or accessibility requirements.
| dataSource={jobs} | ||
| pagination={false}/> | ||
| pagination={false} | ||
| scroll={{y: 400}}/> |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Consider making the fixed scroll height responsive.
The 400px fixed height might not work well on smaller screens or different viewport sizes. Consider calculating height dynamically like in SearchResultsTable.
- scroll={{y: 400}}/>
+ scroll={{y: Math.min(400, window.innerHeight * 0.6)}}/>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| scroll={{y: 400}}/> | |
| scroll={{y: Math.min(400, window.innerHeight * 0.6)}}/> |
🤖 Prompt for AI Agents
In components/log-viewer-webui/client/src/pages/IngestPage/Jobs/index.tsx at
line 93, the scroll height is currently fixed at 400px, which may not be
responsive on different screen sizes. Modify the scroll height to be dynamically
calculated based on the viewport or container size, similar to the approach used
in SearchResultsTable, to ensure better responsiveness across devices.
| const scrollNode = containerRef.current.querySelector<HTMLElement>( | ||
| VIRTUAL_TABLE_HOLDER_SELECTOR | ||
| ); | ||
|
|
||
| if (null === scrollNode) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
CSS selector dependency introduces maintenance risk.
The code relies on antd's internal CSS class which could change in future versions. Consider adding error handling or documentation about this dependency.
const scrollNode = containerRef.current.querySelector<HTMLElement>(
VIRTUAL_TABLE_HOLDER_SELECTOR
);
if (null === scrollNode) {
+ console.warn("VirtualTable: Virtual table holder not found. Ensure antd table has virtual=true");
return;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const scrollNode = containerRef.current.querySelector<HTMLElement>( | |
| VIRTUAL_TABLE_HOLDER_SELECTOR | |
| ); | |
| if (null === scrollNode) { | |
| return; | |
| } | |
| const scrollNode = containerRef.current.querySelector<HTMLElement>( | |
| VIRTUAL_TABLE_HOLDER_SELECTOR | |
| ); | |
| if (null === scrollNode) { | |
| console.warn( | |
| "VirtualTable: Virtual table holder not found. Ensure antd table has virtual=true" | |
| ); | |
| return; | |
| } |
🤖 Prompt for AI Agents
In components/log-viewer-webui/client/src/components/VirtualTable/index.tsx
around lines 32 to 38, the code depends on an internal antd CSS selector which
may change and break functionality. To fix this, add error handling to
gracefully handle cases where the selector is not found, and include a comment
documenting this dependency and the potential risk for future maintainers.
| break; | ||
| case "End": | ||
| // Scroll to the bottom of the table. | ||
| scrollTop = Number.MAX_SAFE_INTEGER; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Use scrollHeight instead of MAX_SAFE_INTEGER for "End" key.
Setting scrollTop to MAX_SAFE_INTEGER may not reliably scroll to the bottom. Use the element's scrollHeight for more predictable behaviour.
case "End":
// Scroll to the bottom of the table.
- scrollTop = Number.MAX_SAFE_INTEGER;
+ scrollTop = scrollNode.scrollHeight - scrollNode.clientHeight;
break;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| scrollTop = Number.MAX_SAFE_INTEGER; | |
| case "End": | |
| // Scroll to the bottom of the table. | |
| scrollTop = scrollNode.scrollHeight - scrollNode.clientHeight; | |
| break; |
🤖 Prompt for AI Agents
In components/log-viewer-webui/client/src/components/VirtualTable/index.tsx at
line 63, replace the assignment of scrollTop from Number.MAX_SAFE_INTEGER to the
element's scrollHeight property. This change ensures that pressing the "End" key
scrolls reliably to the bottom by using the actual scrollable height of the
element instead of an arbitrary large number.
:xMerge branch 'vtable' of https://github.com/davemarco/clp into vtable
junhaoliao
left a comment
There was a problem hiding this comment.
pretty cool. i think we can submit this as a fix to Antd some day
do you think this PR title is more concise?
feat(new-webui): Enable keyboard scrolling in AntD virtual table used by search results and ingest jobs (resolves #944).
| const scrollNode = containerRef.current.querySelector<HTMLElement>( | ||
| VIRTUAL_TABLE_HOLDER_SELECTOR | ||
| ); |
junhaoliao
left a comment
There was a problem hiding this comment.
any thoughts of the previously proposed title?
feat(new-webui): Enable keyboard scrolling in AntD virtual table used by search results and ingest jobs (resolves #944).
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTable/index.tsx(2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}`: - Prefer `false == ` rather than `!`.
**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}: - Preferfalse == <expression>rather than!<expression>.
components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTable/index.tsx
🧬 Code Graph Analysis (1)
components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTable/index.tsx (2)
components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTable/typings.tsx (2)
SearchResult(72-72)searchResultsTableColumns(74-74)components/webui/imports/ui/SearchView/SearchView.jsx (1)
searchResults(108-157)
⏰ Context from checks skipped due to timeout of 90000ms (2)
- GitHub Check: antlr-code-committed (ubuntu-latest)
- GitHub Check: lint-check (ubuntu-latest)
🔇 Additional comments (4)
components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTable/index.tsx (4)
63-63: Good: explicit generic for type safety.Providing
<VirtualTable<SearchResult>>ensures the table’s props and columns align with theSearchResultshape. Strong typing here is beneficial.
65-65: Solid fallback for empty data.Using
dataSource={searchResults || []}avoids null or undefined issues when there are no results. No changes needed.
68-68:Details
❓ Verification inconclusive
Avoid initial zero-height flicker.
With
tableHeightinitialised to0, the table renders at zero height on first paint, causing a brief flicker. Consider switching touseLayoutEffectfor DOM measurements or setting an initial height based on a one-time calculation:-import {useEffect, useRef, useState} from "react"; +import {useLayoutEffect, useRef, useState} from "react"; ... - useEffect(() => { + useLayoutEffect(() => { const updateHeight = () => { /* ... */ }; /* ... */Please verify that this change eliminates any layout jumps on mount.
🏁 Script executed:
#!/usr/bin/env bash set -e FILE="components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTable/index.tsx" echo "----- Searching for useState -----" rg -C3 "useState" "$FILE" || true echo "----- Searching for useEffect -----" rg -C3 "useEffect" "$FILE" || true echo "----- Searching for useLayoutEffect -----" rg -C3 "useLayoutEffect" "$FILE" || trueLength of output: 1789
Switch DOM measurements to useLayoutEffect to prevent initial flicker
Replace the height-calculation effect so it runs before paint, ensuring
tableHeightis set on first render:• File:
components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTable/index.tsx- import {useEffect, useRef, useState} from "react"; + import {useLayoutEffect, useRef, useState} from "react"; ... - useEffect(() => { + useLayoutEffect(() => { const updateHeight = () => { if (containerRef.current) { const {top} = containerRef.current.getBoundingClientRect(); setTableHeight(window.innerHeight - top - PADDING); } }; window.addEventListener("resize", updateHeight); updateHeight(); return () => window.removeEventListener("resize", updateHeight); }, [/* your deps */]);• Leave other effects (e.g. result-count tracking) on
useEffect.Please verify that this change removes any layout jumps on mount.
59-62:Details
❌ Incorrect review comment
Ensure container is focusable for keyboard events.
The
<div>wrapper hasoutline: "none"but notabIndex, so it won’t receive focus or keyboard events by default. AddtabIndex={0}(and moveoutline: noneinto a focus style) so keyboard navigation actually reaches the container:- <div + <div ref={containerRef} + tabIndex={0} style={{outline: "none"}} >
🏁 Script executed:
#!/bin/bash # Verify if the containerRef is used for keyboard handling in SearchResultsTable rg -n "containerRef" components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTable/index.tsx rg -n "onKey" components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTable/index.tsx rg -n "addEventListener" components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTable/index.tsxLength of output: 630
Remove focusability suggestion: no keyboard events are bound to this container
There are currently no keyboard event handlers (e.g., onKeyDown or addEventListener('keydown')) on this
<div>, so addingtabIndex={0}won’t have any effect. If you intend to support keyboard navigation, first implement the necessary event handlers; only then should you make the container focusable.Likely an incorrect or invalid review comment.
|
|
||
| import {Table} from "antd"; | ||
|
|
||
| import VirtualTable from "../../../../components/VirtualTable"; |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Deep relative import path – consider aliasing.
The import for VirtualTable uses a lengthy relative path (../../../../components/VirtualTable), which can be brittle. Define a path alias (e.g. @components/VirtualTable) in your TS config to improve maintainability and readability.
🤖 Prompt for AI Agents
In
components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTable/index.tsx
at line 7, the import of VirtualTable uses a deep relative path which is hard to
maintain. To fix this, define a path alias like '@components' in the TypeScript
configuration (tsconfig.json) pointing to the components directory, then update
the import statement to use this alias instead of the relative path.
…ed by search results and ingest jobs (resolves y-scope#944). (y-scope#972) Co-authored-by: Marco <david.marcovitch@yscope.com>
Description
Antd table with virtual setting does support keyboard scrolling see #944.
Junhao tried modifying scroll element but did not work. I tried a bunch of other things, but then realized we were just not selecting the right scroll element (its not clear from docs, but just looked at rendered elements)
Implementation modifies scrolltop using keyboard events.
I made a wrapper of antd table as new component and imported to search results and ingest jobs. Ingest jobs table may get large, so better to also make virtual...
Checklist
breaking change.
Validation performed
Scrolling works as expected.
Summary by CodeRabbit