Skip to content

Commit

Permalink
New Elements panel (opt-in)
Browse files Browse the repository at this point in the history
  • Loading branch information
bvaughn committed Sep 26, 2023
1 parent 69fa35f commit 53a24a8
Show file tree
Hide file tree
Showing 24 changed files with 658 additions and 127 deletions.
10 changes: 10 additions & 0 deletions packages/replay-next/components/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type IconType =
| "check"
| "checked-rounded"
| "close"
| "collapse"
| "comment"
| "comments"
| "conditional"
Expand All @@ -32,6 +33,7 @@ export type IconType =
| "eager-evaluation"
| "edit"
| "error"
| "expand"
| "fast-forward"
| "file"
| "filter"
Expand Down Expand Up @@ -160,6 +162,10 @@ export default function Icon({
</>
);
break;
case "collapse":
path =
"M19.5,3.09L15,7.59V4H13V11H20V9H16.41L20.91,4.5L19.5,3.09M4,13V15H7.59L3.09,19.5L4.5,20.91L9,16.41V20H11V13H4Z";
break;
case "comment":
path =
"M7.4342 22C7.64309 22 7.83706 21.951 8.01611 21.8531C8.20113 21.7552 8.41898 21.5931 8.66965 21.3667L12.0537 18.2735L17.6132 18.2827C18.5264 18.2827 19.3113 18.1022 19.9678 17.7412C20.6243 17.374 21.1256 16.8539 21.4718 16.1808C21.8239 15.5077 22 14.7061 22 13.776V6.50665C22 5.57656 21.8239 4.77497 21.4718 4.10188C21.1256 3.42879 20.6243 2.91173 19.9678 2.55071C19.3113 2.18357 18.5264 2 17.6132 2H6.38675C5.47359 2 4.68875 2.18357 4.03223 2.55071C3.38168 2.91173 2.88033 3.42879 2.5282 4.10188C2.17607 4.77497 2 5.57656 2 6.50665V13.776C2 14.7061 2.17905 15.5077 2.53715 16.1808C2.90122 16.8539 3.39958 17.371 4.03223 17.732C4.67084 18.093 5.40495 18.2735 6.23456 18.2735H6.48523V20.9169C6.48523 21.2474 6.56879 21.5105 6.7359 21.7063C6.90898 21.9021 7.14175 22 7.4342 22ZM7.6222 10.1413C7.6222 9.82316 7.72068 9.5631 7.91764 9.36117C8.12056 9.15313 8.37123 9.04911 8.66965 9.04911H10.9615V6.6994C10.9615 6.39345 11.06 6.13951 11.2569 5.93759C11.4539 5.72954 11.7046 5.62552 12.009 5.62552C12.3133 5.62552 12.564 5.72954 12.761 5.93759C12.9639 6.13951 13.0654 6.39345 13.0654 6.6994V9.04911H15.3662C15.6646 9.04911 15.9123 9.15313 16.1092 9.36117C16.3062 9.5631 16.4047 9.82316 16.4047 10.1413C16.4047 10.4473 16.3062 10.7043 16.1092 10.9123C15.9123 11.1143 15.6646 11.2152 15.3662 11.2152H13.0654V13.5649C13.0654 13.8709 12.9639 14.1248 12.761 14.3268C12.564 14.5287 12.3133 14.6296 12.009 14.6296C11.7046 14.6296 11.4539 14.5287 11.2569 14.3268C11.06 14.1248 10.9615 13.8709 10.9615 13.5649V11.2152H8.66965C8.37123 11.2152 8.12056 11.1143 7.91764 10.9123C7.72068 10.7043 7.6222 10.4473 7.6222 10.1413Z";
Expand Down Expand Up @@ -238,6 +244,10 @@ export default function Icon({
path =
"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z";
break;
case "expand":
path =
"M10,21V19H6.41L10.91,14.5L9.5,13.09L5,17.59V14H3V21H10M14.5,10.91L19,6.41V10H21V3H14V5H17.59L13.09,9.5L14.5,10.91Z";
break;
case "fast-forward":
path =
"M19.5012 3C18.6769 3 18.0025 3.675 18.0025 4.5V19.5C18.0025 20.325 18.6769 21 19.5012 21C20.3256 21 21 20.325 21 19.5V4.5C21 3.675 20.3256 3 19.5012 3ZM14.0158 13.23L5.36805 19.335C4.3788 20.04 3 19.32 3 18.105V5.895C3 4.68 4.3638 3.975 5.36805 4.665L14.0158 10.77C14.8701 11.37 14.8701 12.63 14.0158 13.23Z";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
.List:focus {
outline: none;
}

.List > div {
/**
* Each row should grow as much as needed to fit its code/content.
* The parent list will measure rows after render and adjust the min-width of the list.
* This prevents horizontal scrolling from jumping as new rows are rendered.
*/
width: var(--longest-line-width) !important;
}
25 changes: 22 additions & 3 deletions packages/replay-next/components/elements/ElementsList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ObjectId, PauseId } from "@replayio/protocol";
import {
CSSProperties,
ForwardedRef,
ReactElement,
useContext,
Expand All @@ -9,8 +10,13 @@ import {
useRef,
useSyncExternalStore,
} from "react";
import { ListOnItemsRenderedProps } from "react-window";
import { useImperativeCacheValue } from "suspense";

import { ElementsListData } from "replay-next/components/elements/ElementsListData";
import { useElementsListCssVariables } from "replay-next/components/elements/hooks/useElementsListCssVariables";
import { useScrollSelectedElementIntoView } from "replay-next/components/elements/hooks/useScrollSelectedElementIntoView";
import { rootObjectIdCache } from "replay-next/components/elements/suspense/RootObjectIdCache";
import { Item } from "replay-next/components/elements/types";
import { DefaultFallback } from "replay-next/components/ErrorBoundary";
import {
Expand All @@ -32,21 +38,30 @@ export function ElementsList({
noContentFallback,
onSelectionChange: onSelectionChangeProp,
pauseId,
rootObjectId,
}: {
height: number;
forwardedRef?: ForwardedRef<ImperativeHandle>;
noContentFallback?: ReactElement;
onSelectionChange?: (id: ObjectId | null) => void;
pauseId: PauseId;
rootObjectId: ObjectId;
}) {
const replayClient = useContext(ReplayClientContext);

const itemData = useMemo<ElementsListItemData>(() => ({}), []);

const genericListRef = useRef<GenericListImperativeHandle>(null);

const { cssVariables, onItemsRendered: onItemsRenderedOne } = useElementsListCssVariables();

const onItemsRenderedTwo = useScrollSelectedElementIntoView();

const onItemsRendered = (props: ListOnItemsRenderedProps) => {
onItemsRenderedOne(props);
onItemsRenderedTwo(props);
};

const { value: rootObjectId } = useImperativeCacheValue(rootObjectIdCache, replayClient, pauseId);

const listData = useMemo<ElementsListData>(
() => new ElementsListData(replayClient, pauseId),
[pauseId, replayClient]
Expand Down Expand Up @@ -82,7 +97,9 @@ export function ElementsList({
}, [listData]);

useEffect(() => {
listData.registerRootNodeId(rootObjectId);
if (rootObjectId) {
listData.registerRootNodeId(rootObjectId);
}
}, [listData, rootObjectId]);

if (didError) {
Expand Down Expand Up @@ -154,8 +171,10 @@ export function ElementsList({
itemRendererComponent={ElementsListItem}
itemSize={ITEM_SIZE}
listData={listData}
onItemsRendered={onItemsRendered}
onKeyDown={onKeyDown}
onSelectionChange={onSelectionChange}
style={cssVariables as CSSProperties}
width="100%"
/>
);
Expand Down
60 changes: 60 additions & 0 deletions packages/replay-next/components/elements/ElementsListData.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,66 @@ describe("ElementsListData", () => {
`);
});

it("should properly parse comments", async () => {
startTest(
`
<html>
<body>
<!-- Single line comment -->
<!--
Multi-line
comment
-->
</body>
</html>
`
);

await listData.registerRootNodeId(rootNodeId, 4);
expect(listData.toString()).toMatchInlineSnapshot(`
"<html>
<head />
<body>
<!-- Single line comment -->
<!-- Multi-line comment -->
</body>
</html>"
`);
});

it("should properly parse iframes and contents", async () => {
const encoded = encodeURIComponent("<html><body><div>This is a div</div></body></html>");
startTest(
`
<html>
<body>
<iframe src="data:text/html,${encoded}"></iframe>
</body>
</html>
`
);

await listData.registerRootNodeId(rootNodeId, 10);
expect(listData.toString()).toMatchInlineSnapshot(`
"<html>
<head />
<body>
<iframe>
#document
<html>
<head />
<body>
<div>
This is a div
</div>
</body>
</html>
</iframe>
</body>
</html>"
`);
});

it("should be able to (re)load already cached data (to support HMR)", async () => {
startTest(
`
Expand Down
16 changes: 13 additions & 3 deletions packages/replay-next/components/elements/ElementsListData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,6 @@ export class ElementsListData extends GenericListData<Item> {
childrenCanBeRendered,
depth = 0,
element,
isExpanded,
subTreeIsFullyLoaded,
subTreeWeight,
} = this.getMutableMetadata(id);
Expand Down Expand Up @@ -221,7 +220,15 @@ export class ElementsListData extends GenericListData<Item> {
let rendered;

if (nodeName.startsWith("#")) {
rendered = node.nodeValue ? node.nodeValue.trim() : "";
let nodeValue = node.nodeValue ?? "";
nodeValue = nodeValue.trim();
nodeValue = nodeValue.replace(/\n\s+/g, " ");

if (node.nodeType === DOM_NODE_CONSTANTS.COMMENT_NODE) {
rendered = `<!-- ${nodeValue} -->`;
} else {
rendered = nodeValue || nodeName;
}
} else {
if (hasChildren) {
if (isExpanded) {
Expand Down Expand Up @@ -332,7 +339,7 @@ export class ElementsListData extends GenericListData<Item> {
depth: metadata.depth + 1,
element: createLoadingPlaceholderElement(currentNodeId),
};
} else if (currentIndex + weight - 1 === index) {
} else if (metadata.hasTail && currentIndex + weight - 1 === index) {
return { ...item, isTail: true };
} else {
currentNodes = metadata.element.filteredChildNodeIds;
Expand Down Expand Up @@ -436,13 +443,16 @@ export class ElementsListData extends GenericListData<Item> {
depth = parentMetadata.depth + 1;
}

const hasTail = element.node.nodeType !== DOM_NODE_CONSTANTS.DOCUMENT_NODE;

const { childrenCanBeRendered, subTreeIsFullyLoaded } =
this.getSubTreeLoadedStatus(element);

this._idToMutableMetadataMap.set(id, {
childrenCanBeRendered,
depth,
element,
hasTail,
isExpanded,
subTreeIsFullyLoaded,
subTreeWeight: 0,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.Node {
min-width: var(--longest-line-width) !important;
line-height: 1rem;
font-family: var(--font-family-monospace);
font-size: var(--font-size-regular-monospace);
Expand Down Expand Up @@ -27,6 +28,7 @@
.Node[data-selected] .HtmlAttribute,
.Node[data-selected] .HTMLBracket,
.Node[data-selected] .HTMLTag,
.Node[data-selected] .Separator,
.Node[data-selected] .Icon {
color: var(--theme-selection-color);
fill: var(--theme-selection-color);
Expand All @@ -43,6 +45,9 @@
width: 1px;
background: var(--background-color-contrast-1);
}
.SelectedSubTreeLine[data-is-selected-node] {
height: 0.5rem;
}

.HTMLTag {
color: var(--value-type-html-tag);
Expand All @@ -58,6 +63,9 @@
margin-left: 1ch;
color: var(--value-type-html-attribute-name);
}
.HtmlAttribute:focus {
outline: none;
}

.Separator {
color: var(--value-type-html-attribute-separator);
Expand All @@ -67,6 +75,12 @@
color: var(--value-type-html-attribute-value);
}

.HtmlAttribute[data-selected],
.HtmlAttributeName[data-selected],
.HtmlAttributeValue[data-selected] {
outline: 1px solid var(--theme-focus-outline-color);
}

.Spacer {
display: inline-block;
width: 1rem;
Expand All @@ -78,19 +92,25 @@
transition: transform 200ms;
width: 1rem;
height: 1rem;
margin-right: 0.5ch;
display: inline-block;
display: inline-flex;
align-items: center;
justify-content: center;
vertical-align: top;
}
.IconContainerRotated {
transform: rotate(90deg);
}

.ArrowIcon,
.LoadingIcon {
width: 1rem;
height: 1rem;
}
.ArrowIcon {
height: 0.75rem;
width: 0.75rem;
fill: var(--color-dimmer);
}

.LoadingIcon {
position: relative;
left: 0.125rem;
Expand All @@ -108,3 +128,8 @@
transform: rotate(360deg);
}
}

.ExpandCollapseIcon {
height: 1rem;
width: 1.5rem;
}
Loading

0 comments on commit 53a24a8

Please sign in to comment.