Files
sanderling/replay-ui/src/panels/HierarchyPanel.tsx
T
pj b44077afde replay ui fix (#56)
* refactor: rename inspect to replay across the codebase

Renames inspect-ui/ to replay-ui/, internal/inspect/ to internal/replay/,
the CLI subcommand from `sanderling inspect` to `sanderling replay`, and
updates all references in docs, Makefile, README, and Go comments.

* feat(replay-ui): show spec filename with full path on hover

RunList and RunDetail now render the basename of spec_path (e.g.
login.spec.ts) with the full path available as a title tooltip.
2026-06-03 16:17:26 +05:30

90 lines
2.5 KiB
TypeScript

import { useMemo, useState } from "react";
import type { HierarchyElement, Hierarchy } from "../types";
import "./HierarchyPanel.css";
interface HierarchyPanelProps {
hierarchy?: Hierarchy;
}
export default function HierarchyPanel({ hierarchy }: HierarchyPanelProps): JSX.Element {
const [filter, setFilter] = useState("");
const elements = hierarchy?.elements ?? [];
const rows = useMemo(() => {
const lower = filter.trim().toLowerCase();
if (!lower) return elements;
return elements.filter((element) => matches(element, lower));
}, [elements, filter]);
if (elements.length === 0) {
return <div className="hierarchy-empty">no hierarchy captured</div>;
}
return (
<div className="hierarchy-panel">
<input
type="text"
className="hierarchy-filter"
placeholder="filter by id, text, desc, class, tag..."
value={filter}
onChange={(e) => setFilter(e.target.value)}
/>
<div className="hierarchy-table-wrap">
<table className="hierarchy-table">
<thead>
<tr>
<th>tag/class</th>
<th>id</th>
<th>text/desc</th>
<th>bounds</th>
</tr>
</thead>
<tbody>
{rows.map((element, index) => (
<HierarchyRow key={index} element={element} />
))}
</tbody>
</table>
</div>
<div className="hierarchy-count">
{rows.length} of {elements.length}
</div>
</div>
);
}
function HierarchyRow({ element }: { element: HierarchyElement }): JSX.Element {
const tag = element.tag ?? element.class ?? "";
const text = (element.text ?? element.description ?? "").slice(0, 80);
const bounds = element.bounds;
return (
<tr>
<td className="hierarchy-tag">{tag}</td>
<td className="hierarchy-id">{element.resourceId ?? ""}</td>
<td className="hierarchy-text">{text}</td>
<td className="hierarchy-bounds">
[{bounds.left},{bounds.top},{bounds.right},{bounds.bottom}]
</td>
</tr>
);
}
function matches(element: HierarchyElement, needle: string): boolean {
const fields = [
element.resourceId,
element.text,
element.description,
element.class,
element.tag,
];
for (const value of fields) {
if (value && value.toLowerCase().includes(needle)) return true;
}
if (element.attributes) {
for (const value of Object.values(element.attributes)) {
if (value.toLowerCase().includes(needle)) return true;
}
}
return false;
}