Add comprehensive column visibility controls to HTTP requests table

- Create ColumnSettings component with collapsible panel and organized column groups
- Implement localStorage persistence for column visibility preferences across sessions
- Add default visible columns: expand, url, start time, total response time, data rate, content-length
- Group columns by category: Basic, Connection, Timing, Performance, Advanced
- Provide bulk actions: Show All, Hide All, Reset to Defaults
- Add conditional rendering for all table headers and cells based on visibility state
- Update RequestRowDetails to dynamically calculate colSpan based on visible columns
- Create responsive grid layout for column settings with hover effects
- Use CSS modules with App.css variables for consistent theming
- Implement type-safe column management with proper TypeScript interfaces

Features:
- 🎛️ Gear icon toggle button for easy access to column settings
- 📁 Logical grouping of related columns for better organization
- 💾 Automatic persistence of user preferences in localStorage
- 🎯 Clean default view showing only essential columns
- 🔧 Flexible customization allowing users to show exactly what they need
- 📱 Responsive design that adapts to different screen sizes

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Michael Mainguy 2025-08-12 09:46:14 -05:00
parent 33cafe695c
commit f9abfbc8ff
6 changed files with 651 additions and 201 deletions

View File

@ -0,0 +1,125 @@
/* Column Settings component styles using CSS variables from App.module.css */
.columnSettings {
margin-bottom: var(--spacing-md);
}
.toggleButton {
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
color: var(--color-text);
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--radius-md);
cursor: pointer;
font-size: var(--font-size-base);
transition: all 0.2s ease;
display: flex;
align-items: center;
gap: var(--spacing-xs);
}
.toggleButton:hover {
background: var(--color-bg-hover);
border-color: var(--color-primary);
color: var(--color-primary);
}
.panel {
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: var(--spacing-lg);
margin-top: var(--spacing-sm);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.panelHeader {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--spacing-lg);
padding-bottom: var(--spacing-md);
border-bottom: 1px solid var(--color-border);
}
.panelHeader h3 {
margin: 0;
color: var(--color-text-highlight);
font-size: var(--font-size-lg);
font-weight: bold;
}
.bulkActions {
display: flex;
gap: var(--spacing-xs);
}
.bulkButton {
background: var(--color-bg-light);
border: 1px solid var(--color-border);
color: var(--color-text);
padding: var(--spacing-xs) var(--spacing-sm);
border-radius: var(--radius-sm);
cursor: pointer;
font-size: var(--font-size-sm);
transition: all 0.2s ease;
}
.bulkButton:hover {
background: var(--color-primary);
color: white;
border-color: var(--color-primary);
}
.columnGroups {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: var(--spacing-lg);
}
.columnGroup {
background: var(--color-bg-light);
border-radius: var(--radius-md);
padding: var(--spacing-md);
}
.groupTitle {
margin: 0 0 var(--spacing-md) 0;
color: var(--color-text-highlight);
font-size: var(--font-size-md);
font-weight: bold;
border-bottom: 1px solid var(--color-border);
padding-bottom: var(--spacing-xs);
}
.columnList {
display: flex;
flex-direction: column;
gap: var(--spacing-xs);
}
.columnItem {
display: flex;
align-items: center;
gap: var(--spacing-xs);
cursor: pointer;
padding: var(--spacing-xs);
border-radius: var(--radius-sm);
transition: background-color 0.2s ease;
}
.columnItem:hover {
background-color: var(--color-bg-hover);
}
.checkbox {
accent-color: var(--color-primary);
cursor: pointer;
}
.columnLabel {
font-size: var(--font-size-base);
color: var(--color-text);
cursor: pointer;
user-select: none;
}

View File

@ -0,0 +1,122 @@
import React from 'react'
import styles from './ColumnSettings.module.css'
interface ColumnConfig {
key: string
label: string
group: string
}
const COLUMN_CONFIGS: ColumnConfig[] = [
// Basic Info
{ key: 'expand', label: 'Expand', group: 'Basic' },
{ key: 'method', label: 'Method', group: 'Basic' },
{ key: 'status', label: 'Status', group: 'Basic' },
{ key: 'type', label: 'Type', group: 'Basic' },
{ key: 'priority', label: 'Priority', group: 'Basic' },
{ key: 'url', label: 'URL', group: 'Basic' },
// Connection Info
{ key: 'connectionNumber', label: 'Connection #', group: 'Connection' },
{ key: 'requestNumber', label: 'Request #', group: 'Connection' },
// Timing
{ key: 'startTime', label: 'Start Time', group: 'Timing' },
{ key: 'queueTime', label: 'Queue Time', group: 'Timing' },
{ key: 'dns', label: 'DNS', group: 'Timing' },
{ key: 'connection', label: 'Connection', group: 'Timing' },
{ key: 'serverLatency', label: 'Server Latency', group: 'Timing' },
{ key: 'duration', label: 'Duration', group: 'Timing' },
{ key: 'totalResponseTime', label: 'Total Response Time', group: 'Timing' },
// Size & Performance
{ key: 'dataRate', label: 'Data Rate', group: 'Performance' },
{ key: 'size', label: 'Size', group: 'Performance' },
{ key: 'contentLength', label: 'Content-Length', group: 'Performance' },
// Advanced
{ key: 'protocol', label: 'Protocol', group: 'Advanced' },
{ key: 'cdn', label: 'CDN', group: 'Advanced' },
{ key: 'cache', label: 'Cache', group: 'Advanced' }
]
interface ColumnSettingsProps {
visibleColumns: Record<string, boolean>
onColumnToggle: (column: string) => void
isOpen: boolean
onToggle: () => void
onShowAll: () => void
onHideAll: () => void
onResetDefaults: () => void
}
const ColumnSettings: React.FC<ColumnSettingsProps> = ({
visibleColumns,
onColumnToggle,
isOpen,
onToggle,
onShowAll,
onHideAll,
onResetDefaults
}) => {
const groups = [...new Set(COLUMN_CONFIGS.map(col => col.group))]
return (
<div className={styles.columnSettings}>
<button
className={styles.toggleButton}
onClick={onToggle}
title="Column Settings"
>
Columns {isOpen ? '▼' : '▶'}
</button>
{isOpen && (
<div className={styles.panel}>
<div className={styles.panelHeader}>
<h3>Column Visibility</h3>
<div className={styles.bulkActions}>
<button onClick={onShowAll} className={styles.bulkButton}>
Show All
</button>
<button onClick={onHideAll} className={styles.bulkButton}>
Hide All
</button>
<button onClick={onResetDefaults} className={styles.bulkButton}>
Reset Defaults
</button>
</div>
</div>
<div className={styles.columnGroups}>
{groups.map(group => (
<div key={group} className={styles.columnGroup}>
<h4 className={styles.groupTitle}>{group}</h4>
<div className={styles.columnList}>
{COLUMN_CONFIGS
.filter(col => col.group === group)
.map(column => (
<label key={column.key} className={styles.columnItem}>
<input
type="checkbox"
checked={visibleColumns[column.key] || false}
onChange={() => onColumnToggle(column.key)}
className={styles.checkbox}
/>
<span className={styles.columnLabel}>
{column.label}
</span>
</label>
))
}
</div>
</div>
))}
</div>
</div>
)}
</div>
)
}
export default ColumnSettings

View File

@ -3,6 +3,7 @@ import { useDatabaseTraceData } from '../../hooks/useDatabaseTraceData'
import { getUrlParams, updateUrlWithTraceId } from '../../App' import { getUrlParams, updateUrlWithTraceId } from '../../App'
import RequestFilters from './RequestFilters' import RequestFilters from './RequestFilters'
import RequestsTable from './RequestsTable' import RequestsTable from './RequestsTable'
import ColumnSettings from './ColumnSettings'
import styles from './HTTPRequestViewer.module.css' import styles from './HTTPRequestViewer.module.css'
// Lazy load 3D viewers to reduce main bundle size // Lazy load 3D viewers to reduce main bundle size
@ -77,6 +78,43 @@ export default function HTTPRequestViewer({ traceId }: HTTPRequestViewerProps) {
const [show3DViewer, setShow3DViewer] = useState(false) const [show3DViewer, setShow3DViewer] = useState(false)
const [showTimelineViewer, setShowTimelineViewer] = useState(false) const [showTimelineViewer, setShowTimelineViewer] = useState(false)
// Column visibility state - default visible columns as requested
const [visibleColumns, setVisibleColumns] = useState(() => {
const saved = localStorage.getItem('httpRequestTableColumns')
if (saved) {
try {
return JSON.parse(saved)
} catch (e) {
console.warn('Failed to parse saved column settings, using defaults')
}
}
// Default visible columns
return {
expand: true,
method: false,
status: false,
type: false,
priority: false,
url: true,
connectionNumber: false,
requestNumber: false,
startTime: true,
queueTime: false,
dns: false,
connection: false,
serverLatency: false,
duration: false,
totalResponseTime: true,
dataRate: true,
size: false,
contentLength: true,
protocol: false,
cdn: false,
cache: false
}
})
const [columnSettingsOpen, setColumnSettingsOpen] = useState(false)
// Initialize 3D view state from URL on component mount and handle URL changes // Initialize 3D view state from URL on component mount and handle URL changes
useEffect(() => { useEffect(() => {
const updateFrom3DUrl = () => { const updateFrom3DUrl = () => {
@ -117,6 +155,62 @@ export default function HTTPRequestViewer({ traceId }: HTTPRequestViewerProps) {
setSsimThreshold(pendingSSIMThreshold) setSsimThreshold(pendingSSIMThreshold)
} }
// Column visibility handlers
const handleColumnToggle = (column: string) => {
const newVisibleColumns = {
...visibleColumns,
[column]: !visibleColumns[column]
}
setVisibleColumns(newVisibleColumns)
localStorage.setItem('httpRequestTableColumns', JSON.stringify(newVisibleColumns))
}
const handleShowAllColumns = () => {
const allVisible = Object.keys(visibleColumns).reduce((acc, key) => {
acc[key] = true
return acc
}, {} as Record<string, boolean>)
setVisibleColumns(allVisible)
localStorage.setItem('httpRequestTableColumns', JSON.stringify(allVisible))
}
const handleHideAllColumns = () => {
const allHidden = Object.keys(visibleColumns).reduce((acc, key) => {
acc[key] = key === 'expand' // Always keep expand column visible
return acc
}, {} as Record<string, boolean>)
setVisibleColumns(allHidden)
localStorage.setItem('httpRequestTableColumns', JSON.stringify(allHidden))
}
const handleResetDefaults = () => {
const defaultColumns = {
expand: true,
method: false,
status: false,
type: false,
priority: false,
url: true,
connectionNumber: false,
requestNumber: false,
startTime: true,
queueTime: false,
dns: false,
connection: false,
serverLatency: false,
duration: false,
totalResponseTime: true,
dataRate: true,
size: false,
contentLength: true,
protocol: false,
cdn: false,
cache: false
}
setVisibleColumns(defaultColumns)
localStorage.setItem('httpRequestTableColumns', JSON.stringify(defaultColumns))
}
const httpRequests = useMemo(() => { const httpRequests = useMemo(() => {
if (!traceData) return [] if (!traceData) return []
const httpRequests = processHTTPRequests(traceData.traceEvents) const httpRequests = processHTTPRequests(traceData.traceEvents)
@ -429,6 +523,17 @@ export default function HTTPRequestViewer({ traceId }: HTTPRequestViewerProps) {
</div> </div>
)} )}
{/* Column Settings */}
<ColumnSettings
visibleColumns={visibleColumns}
onColumnToggle={handleColumnToggle}
isOpen={columnSettingsOpen}
onToggle={() => setColumnSettingsOpen(!columnSettingsOpen)}
onShowAll={handleShowAllColumns}
onHideAll={handleHideAllColumns}
onResetDefaults={handleResetDefaults}
/>
{/* Requests Table */} {/* Requests Table */}
<RequestsTable <RequestsTable
httpRequests={httpRequests} httpRequests={httpRequests}
@ -438,6 +543,7 @@ export default function HTTPRequestViewer({ traceId }: HTTPRequestViewerProps) {
showQueueAnalysis={showQueueAnalysis} showQueueAnalysis={showQueueAnalysis}
expandedRows={expandedRows} expandedRows={expandedRows}
onToggleRowExpansion={toggleRowExpansion} onToggleRowExpansion={toggleRowExpansion}
visibleColumns={visibleColumns}
/> />
</div> </div>
) )

View File

@ -14,12 +14,15 @@ import {
interface RequestRowDetailsProps { interface RequestRowDetailsProps {
request: HTTPRequest request: HTTPRequest
visibleColumns: Record<string, boolean>
} }
const RequestRowDetails: React.FC<RequestRowDetailsProps> = ({ request }) => { const RequestRowDetails: React.FC<RequestRowDetailsProps> = ({ request, visibleColumns }) => {
// Calculate the number of visible columns for colSpan
const visibleColumnCount = Object.values(visibleColumns).filter(Boolean).length
return ( return (
<tr key={`${request.requestId}-expanded`} className={styles.expandedRow}> <tr key={`${request.requestId}-expanded`} className={styles.expandedRow}>
<td colSpan={20}> <td colSpan={visibleColumnCount}>
<div className={styles.expandedContent}> <div className={styles.expandedContent}>
{/* Request Details */} {/* Request Details */}

View File

@ -25,125 +25,174 @@ interface RequestRowSummaryProps {
showQueueAnalysis: boolean showQueueAnalysis: boolean
isExpanded: boolean isExpanded: boolean
onToggleRowExpansion: (requestId: string) => void onToggleRowExpansion: (requestId: string) => void
visibleColumns: Record<string, boolean>
} }
const RequestRowSummary: React.FC<RequestRowSummaryProps> = ({ const RequestRowSummary: React.FC<RequestRowSummaryProps> = ({
request, request,
showQueueAnalysis, showQueueAnalysis,
isExpanded, isExpanded,
onToggleRowExpansion onToggleRowExpansion,
visibleColumns
}) => { }) => {
return ( return (
<> <>
<tr className={styles.default} key={request.requestId} <tr className={styles.default} key={request.requestId}
onClick={() => onToggleRowExpansion(request.requestId)} onClick={() => onToggleRowExpansion(request.requestId)}
> >
<td> {visibleColumns.expand && (
{isExpanded ? '' : '+'} <td>
</td> {isExpanded ? '' : '+'}
<td> </td>
{request.method} )}
</td> {visibleColumns.method && (
<td> <td>
{request.statusCode || '-'} {request.method}
</td> </td>
<td> )}
{request.resourceType} {visibleColumns.status && (
</td> <td>
<td className={styles.priorityCell}> {request.statusCode || '-'}
<span> </td>
{getPriorityIcon(request.priority)} )}
{request.priority || '-'} {visibleColumns.type && (
</span> <td>
</td> {request.resourceType}
<td> </td>
<a href={request.url} target="_blank" rel="noopener noreferrer"> )}
{truncateUrl(request.url)} {visibleColumns.priority && (
</a> <td className={styles.priorityCell}>
</td> <span>
<td> {getPriorityIcon(request.priority)}
{request.connectionNumber || '-'} {request.priority || '-'}
</td> </span>
<td> </td>
{request.requestNumberOnConnection || '-'} )}
</td> {visibleColumns.url && (
<td> <td>
{formatDuration(request.timing.startOffset)} <a href={request.url} target="_blank" rel="noopener noreferrer">
</td> {truncateUrl(request.url)}
<td className={getConnectionClass(request?.timing?.queueTime ||0)}> </a>
<div> </td>
{formatDuration(request.timing.queueTime)} )}
{showQueueAnalysis && request.queueAnalysis && ( {visibleColumns.connectionNumber && (
<span <td>
title={request.queueAnalysis.description} {request.connectionNumber || '-'}
> </td>
{getQueueAnalysisIcon(request.queueAnalysis)} )}
</span> {visibleColumns.requestNumber && (
)} <td>
</div> {request.requestNumberOnConnection || '-'}
</td> </td>
)}
{visibleColumns.startTime && (
<td>
{formatDuration(request.timing.startOffset)}
</td>
)}
{visibleColumns.queueTime && (
<td className={getConnectionClass(request?.timing?.queueTime ||0)}>
<div>
{formatDuration(request.timing.queueTime)}
{showQueueAnalysis && request.queueAnalysis && (
<span
title={request.queueAnalysis.description}
>
{getQueueAnalysisIcon(request.queueAnalysis)}
</span>
)}
</div>
</td>
)}
{/* DNS Time */} {/* DNS Time */}
<td > {visibleColumns.dns && (
{request.timing.dnsStart !== undefined && request.timing.dnsEnd !== undefined && request.timing.dnsStart >= 0 && request.timing.dnsEnd >= 0 <td >
? formatDuration((request.timing.dnsEnd || 0) - (request.timing.dnsStart || 0)) {request.timing.dnsStart !== undefined && request.timing.dnsEnd !== undefined && request.timing.dnsStart >= 0 && request.timing.dnsEnd >= 0
: <span>cached</span> ? formatDuration((request.timing.dnsEnd || 0) - (request.timing.dnsStart || 0))
} : <span>cached</span>
</td> }
</td>
)}
{/* Connection Time */} {/* Connection Time */}
<td className={getConnectionClass((request.timing.connectEnd || 0) - (request.timing.connectStart || 0))}> {visibleColumns.connection && (
{request.timing.connectStart !== undefined && request.timing.connectEnd !== undefined <td className={getConnectionClass((request.timing.connectEnd || 0) - (request.timing.connectStart || 0))}>
&& request.timing.connectStart >= 0 && request.timing.connectEnd >= 0 {request.timing.connectStart !== undefined && request.timing.connectEnd !== undefined
? <span > && request.timing.connectStart >= 0 && request.timing.connectEnd >= 0
{formatDuration((request.timing.connectEnd || 0) - (request.timing.connectStart || 0))} ? <span >
</span> {formatDuration((request.timing.connectEnd || 0) - (request.timing.connectStart || 0))}
: <span> </span>
{request.connectionReused ? 'reused' : 'cached'} : <span>
</span> {request.connectionReused ? 'reused' : 'cached'}
} </span>
</td> }
<td className={`${getServerLatencyClass(request.timing.serverLatency)}`}> </td>
{formatDuration(request.timing.serverLatency)} )}
</td>
<td className={getDurationClass(request.timing.duration)}> {visibleColumns.serverLatency && (
{formatDuration(request.timing.duration)} <td className={`${getServerLatencyClass(request.timing.serverLatency)}`}>
</td> {formatDuration(request.timing.serverLatency)}
</td>
)}
{visibleColumns.duration && (
<td className={getDurationClass(request.timing.duration)}>
{formatDuration(request.timing.duration)}
</td>
)}
{/* Total Response Time */} {/* Total Response Time */}
<td className={`${getTotalResponseTimeClass(request.timing.totalResponseTime)}`}> {visibleColumns.totalResponseTime && (
{formatDuration(request.timing.totalResponseTime)} <td className={`${getTotalResponseTimeClass(request.timing.totalResponseTime)}`}>
</td> {formatDuration(request.timing.totalResponseTime)}
</td>
)}
{/* Data Rate */} {/* Data Rate */}
<td className={`${getDataRateClass(request.encodedDataLength, request.contentLength, request.timing.duration)}`}> {visibleColumns.dataRate && (
{formatDataRate(request.encodedDataLength, request.contentLength, request.timing.duration)} <td className={`${getDataRateClass(request.encodedDataLength, request.contentLength, request.timing.duration)}`}>
</td> {formatDataRate(request.encodedDataLength, request.contentLength, request.timing.duration)}
</td>
)}
<td className={`${getSizeClass(request.encodedDataLength)}`}> {visibleColumns.size && (
{formatSize(request.encodedDataLength)} <td className={`${getSizeClass(request.encodedDataLength)}`}>
</td> {formatSize(request.encodedDataLength)}
<td className={`${getSizeClass(request.contentLength)}`}> </td>
{request.contentLength ? formatSize(request.contentLength) : '-'} )}
</td>
<td className={getProtocolClass(request.protocol)}> {visibleColumns.contentLength && (
{request.protocol || '-'} <td className={`${getSizeClass(request.contentLength)}`}>
</td> {request.contentLength ? formatSize(request.contentLength) : '-'}
<td </td>
title={request.cdnAnalysis ? )}
`${getCDNDisplayName(request.cdnAnalysis.provider)} ${request.cdnAnalysis.isEdge ? '(Edge)' : '(Origin)'} - ${request.cdnAnalysis.detectionMethod}` :
'No CDN detected'} {visibleColumns.protocol && (
> <td className={getProtocolClass(request.protocol)}>
{request.cdnAnalysis ? getCDNIcon(request.cdnAnalysis) : '-'} {request.protocol || '-'}
</td> </td>
<td> )}
{request.fromCache ? '💾' : request.connectionReused ? '🔄' : '🌐'}
</td> {visibleColumns.cdn && (
<td
title={request.cdnAnalysis ?
`${getCDNDisplayName(request.cdnAnalysis.provider)} ${request.cdnAnalysis.isEdge ? '(Edge)' : '(Origin)'} - ${request.cdnAnalysis.detectionMethod}` :
'No CDN detected'}
>
{request.cdnAnalysis ? getCDNIcon(request.cdnAnalysis) : '-'}
</td>
)}
{visibleColumns.cache && (
<td>
{request.fromCache ? '💾' : request.connectionReused ? '🔄' : '🌐'}
</td>
)}
</tr> </tr>
{/* Expanded Row Details */} {/* Expanded Row Details */}
{isExpanded && <RequestRowDetails request={request} />} {isExpanded && <RequestRowDetails request={request} visibleColumns={visibleColumns} />}
</> </>
) )
} }

View File

@ -19,6 +19,7 @@ interface RequestsTableProps {
// Display options // Display options
showQueueAnalysis: boolean showQueueAnalysis: boolean
visibleColumns: Record<string, boolean>
// Row expansion state // Row expansion state
expandedRows: Set<string> expandedRows: Set<string>
@ -31,6 +32,7 @@ const RequestsTable: React.FC<RequestsTableProps> = ({
paginatedTimelineEntries, paginatedTimelineEntries,
paginatedRequests, paginatedRequests,
showQueueAnalysis, showQueueAnalysis,
visibleColumns,
expandedRows, expandedRows,
onToggleRowExpansion onToggleRowExpansion
}) => { }) => {
@ -41,111 +43,153 @@ const RequestsTable: React.FC<RequestsTableProps> = ({
<table className={styles.table}> <table className={styles.table}>
<thead className={styles.tableHeader}> <thead className={styles.tableHeader}>
<tr> <tr>
<th className={`${styles.tableHeaderCell} ${styles.center} ${styles.expandColumn}`}> {visibleColumns.expand && (
<Tooltip type={TooltipType.EXPAND_ROW}> <th className={`${styles.tableHeaderCell} ${styles.center} ${styles.expandColumn}`}>
Expand <Tooltip type={TooltipType.EXPAND_ROW}>
</Tooltip> Expand
</th> </Tooltip>
<th className={`${styles.tableHeaderCell} ${styles.left}`}> </th>
<Tooltip type={TooltipType.HTTP_METHOD}> )}
Method {visibleColumns.method && (
</Tooltip> <th className={`${styles.tableHeaderCell} ${styles.left}`}>
</th> <Tooltip type={TooltipType.HTTP_METHOD}>
<th className={`${styles.tableHeaderCell} ${styles.center}`}> Method
<Tooltip type={TooltipType.HTTP_STATUS}> </Tooltip>
Status </th>
</Tooltip> )}
</th> {visibleColumns.status && (
<th className={`${styles.tableHeaderCell} ${styles.left}`}> <th className={`${styles.tableHeaderCell} ${styles.center}`}>
<Tooltip type={TooltipType.RESOURCE_TYPE}> <Tooltip type={TooltipType.HTTP_STATUS}>
Type Status
</Tooltip> </Tooltip>
</th> </th>
<th className={`${styles.tableHeaderCell} ${styles.center}`}> )}
<Tooltip type={TooltipType.REQUEST_PRIORITY}> {visibleColumns.type && (
Priority <th className={`${styles.tableHeaderCell} ${styles.left}`}>
</Tooltip> <Tooltip type={TooltipType.RESOURCE_TYPE}>
</th> Type
<th className={`${styles.tableHeaderCell} ${styles.left}`}> </Tooltip>
<Tooltip type={TooltipType.REQUEST_URL}> </th>
URL )}
</Tooltip> {visibleColumns.priority && (
</th> <th className={`${styles.tableHeaderCell} ${styles.center}`}>
<th className={`${styles.tableHeaderCell} ${styles.center}`}> <Tooltip type={TooltipType.REQUEST_PRIORITY}>
<Tooltip type={TooltipType.CONNECTION_NUMBER}> Priority
Connection # </Tooltip>
</Tooltip> </th>
</th> )}
<th className={`${styles.tableHeaderCell} ${styles.center}`}> {visibleColumns.url && (
<Tooltip type={TooltipType.REQUEST_NUMBER}> <th className={`${styles.tableHeaderCell} ${styles.left}`}>
Request # <Tooltip type={TooltipType.REQUEST_URL}>
</Tooltip> URL
</th> </Tooltip>
<th className={`${styles.tableHeaderCell} ${styles.right}`}> </th>
<Tooltip type={TooltipType.START_TIME}> )}
Start Time {visibleColumns.connectionNumber && (
</Tooltip> <th className={`${styles.tableHeaderCell} ${styles.center}`}>
</th> <Tooltip type={TooltipType.CONNECTION_NUMBER}>
<th className={`${styles.tableHeaderCell} ${styles.right}`}> Connection #
<Tooltip type={TooltipType.QUEUE_TIME}> </Tooltip>
Queue Time </th>
</Tooltip> )}
</th> {visibleColumns.requestNumber && (
<th className={`${styles.tableHeaderCell} ${styles.right}`}> <th className={`${styles.tableHeaderCell} ${styles.center}`}>
<Tooltip type={TooltipType.DNS_TIME}> <Tooltip type={TooltipType.REQUEST_NUMBER}>
DNS Request #
</Tooltip> </Tooltip>
</th> </th>
<th className={`${styles.tableHeaderCell} ${styles.right}`}> )}
<Tooltip type={TooltipType.CONNECTION_TIME}> {visibleColumns.startTime && (
Connection <th className={`${styles.tableHeaderCell} ${styles.right}`}>
</Tooltip> <Tooltip type={TooltipType.START_TIME}>
</th> Start Time
<th className={`${styles.tableHeaderCell} ${styles.right}`}> </Tooltip>
<Tooltip type={TooltipType.SERVER_LATENCY}> </th>
Server Latency )}
</Tooltip> {visibleColumns.queueTime && (
</th> <th className={`${styles.tableHeaderCell} ${styles.right}`}>
<th className={`${styles.tableHeaderCell} ${styles.right}`}> <Tooltip type={TooltipType.QUEUE_TIME}>
<Tooltip type={TooltipType.REQUEST_DURATION}> Queue Time
Duration </Tooltip>
</Tooltip> </th>
</th> )}
<th className={`${styles.tableHeaderCell} ${styles.right}`}> {visibleColumns.dns && (
<Tooltip type={TooltipType.TOTAL_RESPONSE_TIME}> <th className={`${styles.tableHeaderCell} ${styles.right}`}>
Total Response Time <Tooltip type={TooltipType.DNS_TIME}>
</Tooltip> DNS
</th> </Tooltip>
<th className={`${styles.tableHeaderCell} ${styles.right}`}> </th>
<Tooltip type={TooltipType.DATA_RATE}> )}
Data Rate {visibleColumns.connection && (
</Tooltip> <th className={`${styles.tableHeaderCell} ${styles.right}`}>
</th> <Tooltip type={TooltipType.CONNECTION_TIME}>
<th className={`${styles.tableHeaderCell} ${styles.right}`}> Connection
<Tooltip type={TooltipType.TRANSFER_SIZE}> </Tooltip>
Size </th>
</Tooltip> )}
</th> {visibleColumns.serverLatency && (
<th className={`${styles.tableHeaderCell} ${styles.right}`}> <th className={`${styles.tableHeaderCell} ${styles.right}`}>
<Tooltip type={TooltipType.CONTENT_LENGTH}> <Tooltip type={TooltipType.SERVER_LATENCY}>
Content-Length Server Latency
</Tooltip> </Tooltip>
</th> </th>
<th className={`${styles.tableHeaderCell} ${styles.center}`}> )}
<Tooltip type={TooltipType.HTTP_PROTOCOL}> {visibleColumns.duration && (
Protocol <th className={`${styles.tableHeaderCell} ${styles.right}`}>
</Tooltip> <Tooltip type={TooltipType.REQUEST_DURATION}>
</th> Duration
<th className={`${styles.tableHeaderCell} ${styles.center}`}> </Tooltip>
<Tooltip type={TooltipType.CDN_DETECTION}> </th>
CDN )}
</Tooltip> {visibleColumns.totalResponseTime && (
</th> <th className={`${styles.tableHeaderCell} ${styles.right}`}>
<th className={`${styles.tableHeaderCell} ${styles.center}`}> <Tooltip type={TooltipType.TOTAL_RESPONSE_TIME}>
<Tooltip type={TooltipType.CACHE_STATUS}> Total Response Time
Cache </Tooltip>
</Tooltip> </th>
</th> )}
{visibleColumns.dataRate && (
<th className={`${styles.tableHeaderCell} ${styles.right}`}>
<Tooltip type={TooltipType.DATA_RATE}>
Data Rate
</Tooltip>
</th>
)}
{visibleColumns.size && (
<th className={`${styles.tableHeaderCell} ${styles.right}`}>
<Tooltip type={TooltipType.TRANSFER_SIZE}>
Size
</Tooltip>
</th>
)}
{visibleColumns.contentLength && (
<th className={`${styles.tableHeaderCell} ${styles.right}`}>
<Tooltip type={TooltipType.CONTENT_LENGTH}>
Content-Length
</Tooltip>
</th>
)}
{visibleColumns.protocol && (
<th className={`${styles.tableHeaderCell} ${styles.center}`}>
<Tooltip type={TooltipType.HTTP_PROTOCOL}>
Protocol
</Tooltip>
</th>
)}
{visibleColumns.cdn && (
<th className={`${styles.tableHeaderCell} ${styles.center}`}>
<Tooltip type={TooltipType.CDN_DETECTION}>
CDN
</Tooltip>
</th>
)}
{visibleColumns.cache && (
<th className={`${styles.tableHeaderCell} ${styles.center}`}>
<Tooltip type={TooltipType.CACHE_STATUS}>
Cache
</Tooltip>
</th>
)}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -173,6 +217,7 @@ const RequestsTable: React.FC<RequestsTableProps> = ({
showQueueAnalysis={showQueueAnalysis} showQueueAnalysis={showQueueAnalysis}
isExpanded={isExpanded} isExpanded={isExpanded}
onToggleRowExpansion={onToggleRowExpansion} onToggleRowExpansion={onToggleRowExpansion}
visibleColumns={visibleColumns}
/> />
) )
})} })}