Add comprehensive column sorting with icons and tooltips to RequestBreakdown
Enhance all breakdown sections (Resource Type, Status Code, Hostname) with: - Clickable column headers for sorting by any field - Visual sort indicators: neutral (⇅), ascending (▲), descending (▼) - Interactive tooltips showing current sort state and available actions - Default sort by request count in descending order - Toggle between ascending/descending by clicking same column - Consistent styling with brand colors and hover effects - Proper flexbox layout for icon positioning All 8 columns are sortable: name, count, count %, size, size %, response time, response time %, and average response time. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
1035b28472
commit
b5b7aafca5
@ -1,21 +1,121 @@
|
||||
import React from 'react'
|
||||
import styles from './RequestBreakdown.module.css'
|
||||
|
||||
export type SortColumn = 'name' | 'count' | 'percentage' | 'totalSize' | 'sizePercentage' | 'totalResponseTime' | 'responseTimePercentage' | 'averageResponseTime'
|
||||
export type SortDirection = 'asc' | 'desc'
|
||||
|
||||
interface BreakdownTableHeaderProps {
|
||||
categoryLabel: string
|
||||
sortColumn: SortColumn | null
|
||||
sortDirection: SortDirection
|
||||
onSort: (column: SortColumn) => void
|
||||
}
|
||||
|
||||
const BreakdownTableHeader: React.FC<BreakdownTableHeaderProps> = ({ categoryLabel }) => {
|
||||
const BreakdownTableHeader: React.FC<BreakdownTableHeaderProps> = ({
|
||||
categoryLabel,
|
||||
sortColumn,
|
||||
sortDirection,
|
||||
onSort
|
||||
}) => {
|
||||
const getSortIcon = (column: SortColumn) => {
|
||||
if (sortColumn !== column) {
|
||||
return <span className={styles.sortIconNeutral}>⇅</span>
|
||||
}
|
||||
return sortDirection === 'asc' ?
|
||||
<span className={styles.sortIconAsc}>▲</span> :
|
||||
<span className={styles.sortIconDesc}>▼</span>
|
||||
}
|
||||
|
||||
const getTooltipText = (column: SortColumn, label: string) => {
|
||||
if (sortColumn === column) {
|
||||
const oppositeDirection = sortDirection === 'asc' ? 'descending' : 'ascending'
|
||||
return `Click to sort ${label.toLowerCase()} ${oppositeDirection}`
|
||||
}
|
||||
return `Click to sort by ${label.toLowerCase()}`
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.tableHeader}>
|
||||
<span>{categoryLabel}</span>
|
||||
<span>Request Count</span>
|
||||
<span>Request Count %</span>
|
||||
<span>Total Size</span>
|
||||
<span>Total Size %</span>
|
||||
<span>Total Response Time</span>
|
||||
<span>Total Response Time %</span>
|
||||
<span>Avg Response Time</span>
|
||||
<button
|
||||
className={styles.sortButton}
|
||||
onClick={() => onSort('name')}
|
||||
title={getTooltipText('name', categoryLabel)}
|
||||
>
|
||||
<span className={styles.sortButtonContent}>
|
||||
{categoryLabel}
|
||||
{getSortIcon('name')}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className={styles.sortButton}
|
||||
onClick={() => onSort('count')}
|
||||
title={getTooltipText('count', 'Request Count')}
|
||||
>
|
||||
<span className={styles.sortButtonContent}>
|
||||
Request Count
|
||||
{getSortIcon('count')}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className={styles.sortButton}
|
||||
onClick={() => onSort('percentage')}
|
||||
title={getTooltipText('percentage', 'Request Count %')}
|
||||
>
|
||||
<span className={styles.sortButtonContent}>
|
||||
Request Count %
|
||||
{getSortIcon('percentage')}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className={styles.sortButton}
|
||||
onClick={() => onSort('totalSize')}
|
||||
title={getTooltipText('totalSize', 'Total Size')}
|
||||
>
|
||||
<span className={styles.sortButtonContent}>
|
||||
Total Size
|
||||
{getSortIcon('totalSize')}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className={styles.sortButton}
|
||||
onClick={() => onSort('sizePercentage')}
|
||||
title={getTooltipText('sizePercentage', 'Total Size %')}
|
||||
>
|
||||
<span className={styles.sortButtonContent}>
|
||||
Total Size %
|
||||
{getSortIcon('sizePercentage')}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className={styles.sortButton}
|
||||
onClick={() => onSort('totalResponseTime')}
|
||||
title={getTooltipText('totalResponseTime', 'Total Response Time')}
|
||||
>
|
||||
<span className={styles.sortButtonContent}>
|
||||
Total Response Time
|
||||
{getSortIcon('totalResponseTime')}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className={styles.sortButton}
|
||||
onClick={() => onSort('responseTimePercentage')}
|
||||
title={getTooltipText('responseTimePercentage', 'Total Response Time %')}
|
||||
>
|
||||
<span className={styles.sortButtonContent}>
|
||||
Total Response Time %
|
||||
{getSortIcon('responseTimePercentage')}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className={styles.sortButton}
|
||||
onClick={() => onSort('averageResponseTime')}
|
||||
title={getTooltipText('averageResponseTime', 'Average Response Time')}
|
||||
>
|
||||
<span className={styles.sortButtonContent}>
|
||||
Avg Response Time
|
||||
{getSortIcon('averageResponseTime')}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import type { HTTPRequest } from '../httprequestviewer/types/httpRequest'
|
||||
import BreakdownTableHeader from './BreakdownTableHeader'
|
||||
import BreakdownTableHeader, { type SortColumn, type SortDirection } from './BreakdownTableHeader'
|
||||
import styles from './RequestBreakdown.module.css'
|
||||
|
||||
interface CategoryBreakdown {
|
||||
@ -20,6 +20,8 @@ interface HostnameBreakdownProps {
|
||||
|
||||
const HostnameBreakdown: React.FC<HostnameBreakdownProps> = ({ httpRequests }) => {
|
||||
const [showAllHostnames, setShowAllHostnames] = useState(false)
|
||||
const [sortColumn, setSortColumn] = useState<SortColumn | null>('count')
|
||||
const [sortDirection, setSortDirection] = useState<SortDirection>('desc')
|
||||
|
||||
const formatSize = (bytes: number): string => {
|
||||
if (bytes < 1024) return `${bytes} B`
|
||||
@ -33,7 +35,7 @@ const HostnameBreakdown: React.FC<HostnameBreakdownProps> = ({ httpRequests }) =
|
||||
return `${(microseconds / 1000000).toFixed(2)} s`
|
||||
}
|
||||
|
||||
const hostnameBreakdown: CategoryBreakdown[] = useMemo(() => {
|
||||
const baseHostnameBreakdown: CategoryBreakdown[] = useMemo(() => {
|
||||
const hostMap = new Map<string, HTTPRequest[]>()
|
||||
|
||||
httpRequests.forEach(req => {
|
||||
@ -72,15 +74,83 @@ const HostnameBreakdown: React.FC<HostnameBreakdownProps> = ({ httpRequests }) =
|
||||
responseTimePercentage: totalAllResponseTime > 0 ? (totalResponseTime / totalAllResponseTime) * 100 : 0,
|
||||
averageResponseTime: totalResponseTime / requests.length
|
||||
}
|
||||
}).sort((a, b) => b.count - a.count)
|
||||
})
|
||||
}, [httpRequests])
|
||||
|
||||
const handleSort = (column: SortColumn) => {
|
||||
if (sortColumn === column) {
|
||||
setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc')
|
||||
} else {
|
||||
setSortColumn(column)
|
||||
setSortDirection('desc')
|
||||
}
|
||||
}
|
||||
|
||||
const sortedHostnameBreakdown = useMemo(() => {
|
||||
if (!sortColumn) return baseHostnameBreakdown
|
||||
|
||||
return [...baseHostnameBreakdown].sort((a, b) => {
|
||||
let aValue: string | number
|
||||
let bValue: string | number
|
||||
|
||||
switch (sortColumn) {
|
||||
case 'name':
|
||||
aValue = a.name
|
||||
bValue = b.name
|
||||
break
|
||||
case 'count':
|
||||
aValue = a.count
|
||||
bValue = b.count
|
||||
break
|
||||
case 'percentage':
|
||||
aValue = a.percentage
|
||||
bValue = b.percentage
|
||||
break
|
||||
case 'totalSize':
|
||||
aValue = a.totalSize
|
||||
bValue = b.totalSize
|
||||
break
|
||||
case 'sizePercentage':
|
||||
aValue = a.sizePercentage
|
||||
bValue = b.sizePercentage
|
||||
break
|
||||
case 'totalResponseTime':
|
||||
aValue = a.totalResponseTime
|
||||
bValue = b.totalResponseTime
|
||||
break
|
||||
case 'responseTimePercentage':
|
||||
aValue = a.responseTimePercentage
|
||||
bValue = b.responseTimePercentage
|
||||
break
|
||||
case 'averageResponseTime':
|
||||
aValue = a.averageResponseTime
|
||||
bValue = b.averageResponseTime
|
||||
break
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
|
||||
if (typeof aValue === 'string' && typeof bValue === 'string') {
|
||||
const comparison = aValue.localeCompare(bValue)
|
||||
return sortDirection === 'asc' ? comparison : -comparison
|
||||
} else {
|
||||
const comparison = (aValue as number) - (bValue as number)
|
||||
return sortDirection === 'asc' ? comparison : -comparison
|
||||
}
|
||||
})
|
||||
}, [baseHostnameBreakdown, sortColumn, sortDirection])
|
||||
|
||||
return (
|
||||
<div className={styles.breakdownSection}>
|
||||
<h3>By Hostname</h3>
|
||||
<div className={styles.breakdownTable}>
|
||||
<BreakdownTableHeader categoryLabel="Hostname" />
|
||||
{(showAllHostnames ? hostnameBreakdown : hostnameBreakdown.slice(0, 10)).map(item => (
|
||||
<BreakdownTableHeader
|
||||
categoryLabel="Hostname"
|
||||
sortColumn={sortColumn}
|
||||
sortDirection={sortDirection}
|
||||
onSort={handleSort}
|
||||
/>
|
||||
{(showAllHostnames ? sortedHostnameBreakdown : sortedHostnameBreakdown.slice(0, 10)).map(item => (
|
||||
<div key={item.name} className={styles.tableRow}>
|
||||
<span className={styles.categoryName}>{item.name}</span>
|
||||
<span>{item.count}</span>
|
||||
@ -117,11 +187,11 @@ const HostnameBreakdown: React.FC<HostnameBreakdownProps> = ({ httpRequests }) =
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{hostnameBreakdown.length > 10 && (
|
||||
{sortedHostnameBreakdown.length > 10 && (
|
||||
<div className={styles.moreInfo}>
|
||||
{showAllHostnames ? (
|
||||
<>
|
||||
Showing all {hostnameBreakdown.length} hostnames
|
||||
Showing all {sortedHostnameBreakdown.length} hostnames
|
||||
<button
|
||||
onClick={() => setShowAllHostnames(false)}
|
||||
className={styles.toggleButton}
|
||||
@ -131,7 +201,7 @@ const HostnameBreakdown: React.FC<HostnameBreakdownProps> = ({ httpRequests }) =
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Showing top 10 of {hostnameBreakdown.length} hostnames
|
||||
Showing top 10 of {sortedHostnameBreakdown.length} hostnames
|
||||
<button
|
||||
onClick={() => setShowAllHostnames(true)}
|
||||
className={styles.toggleButton}
|
||||
|
@ -107,6 +107,61 @@
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.sortButton {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
color: inherit;
|
||||
text-transform: inherit;
|
||||
letter-spacing: inherit;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
padding: 0;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.sortButton:hover {
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.sortButtonContent {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.sortIconNeutral,
|
||||
.sortIconAsc,
|
||||
.sortIconDesc {
|
||||
font-size: 12px;
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.sortIconNeutral {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.sortIconAsc,
|
||||
.sortIconDesc {
|
||||
opacity: 0.8;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.sortButton:hover .sortIconNeutral {
|
||||
opacity: 0.8;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.sortButton:hover .sortIconAsc,
|
||||
.sortButton:hover .sortIconDesc {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.tableRow {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr 1fr 1.5fr 1fr 1.5fr 1fr 1.5fr;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import type { HTTPRequest } from '../httprequestviewer/types/httpRequest'
|
||||
import BreakdownTableHeader from './BreakdownTableHeader'
|
||||
import BreakdownTableHeader, { type SortColumn, type SortDirection } from './BreakdownTableHeader'
|
||||
import styles from './RequestBreakdown.module.css'
|
||||
|
||||
interface CategoryBreakdown {
|
||||
@ -19,6 +19,8 @@ interface ResourceTypeBreakdownProps {
|
||||
}
|
||||
|
||||
const ResourceTypeBreakdown: React.FC<ResourceTypeBreakdownProps> = ({ httpRequests }) => {
|
||||
const [sortColumn, setSortColumn] = useState<SortColumn | null>('count')
|
||||
const [sortDirection, setSortDirection] = useState<SortDirection>('desc')
|
||||
const formatSize = (bytes: number): string => {
|
||||
if (bytes < 1024) return `${bytes} B`
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
|
||||
@ -31,7 +33,7 @@ const ResourceTypeBreakdown: React.FC<ResourceTypeBreakdownProps> = ({ httpReque
|
||||
return `${(microseconds / 1000000).toFixed(2)} s`
|
||||
}
|
||||
|
||||
const resourceTypeBreakdown: CategoryBreakdown[] = useMemo(() => {
|
||||
const baseResourceTypeBreakdown: CategoryBreakdown[] = useMemo(() => {
|
||||
const typeMap = new Map<string, HTTPRequest[]>()
|
||||
|
||||
httpRequests.forEach(req => {
|
||||
@ -70,15 +72,83 @@ const ResourceTypeBreakdown: React.FC<ResourceTypeBreakdownProps> = ({ httpReque
|
||||
responseTimePercentage: totalAllResponseTime > 0 ? (totalResponseTime / totalAllResponseTime) * 100 : 0,
|
||||
averageResponseTime: totalResponseTime / requests.length
|
||||
}
|
||||
}).sort((a, b) => b.count - a.count)
|
||||
})
|
||||
}, [httpRequests])
|
||||
|
||||
const handleSort = (column: SortColumn) => {
|
||||
if (sortColumn === column) {
|
||||
setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc')
|
||||
} else {
|
||||
setSortColumn(column)
|
||||
setSortDirection('desc')
|
||||
}
|
||||
}
|
||||
|
||||
const sortedResourceTypeBreakdown = useMemo(() => {
|
||||
if (!sortColumn) return baseResourceTypeBreakdown
|
||||
|
||||
return [...baseResourceTypeBreakdown].sort((a, b) => {
|
||||
let aValue: string | number
|
||||
let bValue: string | number
|
||||
|
||||
switch (sortColumn) {
|
||||
case 'name':
|
||||
aValue = a.name
|
||||
bValue = b.name
|
||||
break
|
||||
case 'count':
|
||||
aValue = a.count
|
||||
bValue = b.count
|
||||
break
|
||||
case 'percentage':
|
||||
aValue = a.percentage
|
||||
bValue = b.percentage
|
||||
break
|
||||
case 'totalSize':
|
||||
aValue = a.totalSize
|
||||
bValue = b.totalSize
|
||||
break
|
||||
case 'sizePercentage':
|
||||
aValue = a.sizePercentage
|
||||
bValue = b.sizePercentage
|
||||
break
|
||||
case 'totalResponseTime':
|
||||
aValue = a.totalResponseTime
|
||||
bValue = b.totalResponseTime
|
||||
break
|
||||
case 'responseTimePercentage':
|
||||
aValue = a.responseTimePercentage
|
||||
bValue = b.responseTimePercentage
|
||||
break
|
||||
case 'averageResponseTime':
|
||||
aValue = a.averageResponseTime
|
||||
bValue = b.averageResponseTime
|
||||
break
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
|
||||
if (typeof aValue === 'string' && typeof bValue === 'string') {
|
||||
const comparison = aValue.localeCompare(bValue)
|
||||
return sortDirection === 'asc' ? comparison : -comparison
|
||||
} else {
|
||||
const comparison = (aValue as number) - (bValue as number)
|
||||
return sortDirection === 'asc' ? comparison : -comparison
|
||||
}
|
||||
})
|
||||
}, [baseResourceTypeBreakdown, sortColumn, sortDirection])
|
||||
|
||||
return (
|
||||
<div className={styles.breakdownSection}>
|
||||
<h3>By Resource Type</h3>
|
||||
<div className={styles.breakdownTable}>
|
||||
<BreakdownTableHeader categoryLabel="Resource Type" />
|
||||
{resourceTypeBreakdown.map(item => (
|
||||
<BreakdownTableHeader
|
||||
categoryLabel="Resource Type"
|
||||
sortColumn={sortColumn}
|
||||
sortDirection={sortDirection}
|
||||
onSort={handleSort}
|
||||
/>
|
||||
{sortedResourceTypeBreakdown.map(item => (
|
||||
<div key={item.name} className={styles.tableRow}>
|
||||
<span className={styles.categoryName}>{item.name}</span>
|
||||
<span>{item.count}</span>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import type { HTTPRequest } from '../httprequestviewer/types/httpRequest'
|
||||
import BreakdownTableHeader from './BreakdownTableHeader'
|
||||
import BreakdownTableHeader, { type SortColumn, type SortDirection } from './BreakdownTableHeader'
|
||||
import styles from './RequestBreakdown.module.css'
|
||||
|
||||
interface CategoryBreakdown {
|
||||
@ -19,6 +19,8 @@ interface StatusCodeBreakdownProps {
|
||||
}
|
||||
|
||||
const StatusCodeBreakdown: React.FC<StatusCodeBreakdownProps> = ({ httpRequests }) => {
|
||||
const [sortColumn, setSortColumn] = useState<SortColumn | null>('count')
|
||||
const [sortDirection, setSortDirection] = useState<SortDirection>('desc')
|
||||
const formatSize = (bytes: number): string => {
|
||||
if (bytes < 1024) return `${bytes} B`
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
|
||||
@ -31,7 +33,7 @@ const StatusCodeBreakdown: React.FC<StatusCodeBreakdownProps> = ({ httpRequests
|
||||
return `${(microseconds / 1000000).toFixed(2)} s`
|
||||
}
|
||||
|
||||
const statusCodeBreakdown: CategoryBreakdown[] = useMemo(() => {
|
||||
const baseStatusCodeBreakdown: CategoryBreakdown[] = useMemo(() => {
|
||||
const statusMap = new Map<string, HTTPRequest[]>()
|
||||
|
||||
httpRequests.forEach(req => {
|
||||
@ -70,15 +72,83 @@ const StatusCodeBreakdown: React.FC<StatusCodeBreakdownProps> = ({ httpRequests
|
||||
responseTimePercentage: totalAllResponseTime > 0 ? (totalResponseTime / totalAllResponseTime) * 100 : 0,
|
||||
averageResponseTime: totalResponseTime / requests.length
|
||||
}
|
||||
}).sort((a, b) => b.count - a.count)
|
||||
})
|
||||
}, [httpRequests])
|
||||
|
||||
const handleSort = (column: SortColumn) => {
|
||||
if (sortColumn === column) {
|
||||
setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc')
|
||||
} else {
|
||||
setSortColumn(column)
|
||||
setSortDirection('desc')
|
||||
}
|
||||
}
|
||||
|
||||
const sortedStatusCodeBreakdown = useMemo(() => {
|
||||
if (!sortColumn) return baseStatusCodeBreakdown
|
||||
|
||||
return [...baseStatusCodeBreakdown].sort((a, b) => {
|
||||
let aValue: string | number
|
||||
let bValue: string | number
|
||||
|
||||
switch (sortColumn) {
|
||||
case 'name':
|
||||
aValue = a.name
|
||||
bValue = b.name
|
||||
break
|
||||
case 'count':
|
||||
aValue = a.count
|
||||
bValue = b.count
|
||||
break
|
||||
case 'percentage':
|
||||
aValue = a.percentage
|
||||
bValue = b.percentage
|
||||
break
|
||||
case 'totalSize':
|
||||
aValue = a.totalSize
|
||||
bValue = b.totalSize
|
||||
break
|
||||
case 'sizePercentage':
|
||||
aValue = a.sizePercentage
|
||||
bValue = b.sizePercentage
|
||||
break
|
||||
case 'totalResponseTime':
|
||||
aValue = a.totalResponseTime
|
||||
bValue = b.totalResponseTime
|
||||
break
|
||||
case 'responseTimePercentage':
|
||||
aValue = a.responseTimePercentage
|
||||
bValue = b.responseTimePercentage
|
||||
break
|
||||
case 'averageResponseTime':
|
||||
aValue = a.averageResponseTime
|
||||
bValue = b.averageResponseTime
|
||||
break
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
|
||||
if (typeof aValue === 'string' && typeof bValue === 'string') {
|
||||
const comparison = aValue.localeCompare(bValue)
|
||||
return sortDirection === 'asc' ? comparison : -comparison
|
||||
} else {
|
||||
const comparison = (aValue as number) - (bValue as number)
|
||||
return sortDirection === 'asc' ? comparison : -comparison
|
||||
}
|
||||
})
|
||||
}, [baseStatusCodeBreakdown, sortColumn, sortDirection])
|
||||
|
||||
return (
|
||||
<div className={styles.breakdownSection}>
|
||||
<h3>By Status Code</h3>
|
||||
<div className={styles.breakdownTable}>
|
||||
<BreakdownTableHeader categoryLabel="Status Code" />
|
||||
{statusCodeBreakdown.map(item => (
|
||||
<BreakdownTableHeader
|
||||
categoryLabel="Status Code"
|
||||
sortColumn={sortColumn}
|
||||
sortDirection={sortDirection}
|
||||
onSort={handleSort}
|
||||
/>
|
||||
{sortedStatusCodeBreakdown.map(item => (
|
||||
<div key={item.name} className={styles.tableRow}>
|
||||
<span className={styles.categoryName}>{item.name}</span>
|
||||
<span>{item.count}</span>
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
/**
|
||||
* Chrome DevTools Performance Trace Format
|
||||
* Based on the Trace Event Format specification
|
||||
|
Loading…
Reference in New Issue
Block a user