Skip to content

Commit

Permalink
Pre-process history for history display so we can write unit tests fo…
Browse files Browse the repository at this point in the history
…r it
  • Loading branch information
abi committed Dec 12, 2023
1 parent 9f064c5 commit b883201
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 45 deletions.
57 changes: 14 additions & 43 deletions frontend/src/components/history/HistoryDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { History, HistoryItemType } from "./history_types";
import { History } from "./history_types";
import toast from "react-hot-toast";
import classNames from "classnames";
import {
Expand All @@ -7,6 +7,7 @@ import {
HoverCardContent,
} from "../ui/hover-card";
import { Badge } from "../ui/badge";
import { renderHistory } from "./utils";

interface Props {
history: History;
Expand All @@ -15,42 +16,28 @@ interface Props {
shouldDisableReverts: boolean;
}

function displayHistoryItemType(itemType: HistoryItemType) {
switch (itemType) {
case "ai_create":
return "Create";
case "ai_edit":
return "Edit";
case "code_create":
return "Imported from code";
default: {
const exhaustiveCheck: never = itemType;
throw new Error(`Unhandled case: ${exhaustiveCheck}`);
}
}
}

export default function HistoryDisplay({
history,
currentVersion,
revertToVersion,
shouldDisableReverts,
}: Props) {
return history.length === 0 ? null : (
const renderedHistory = renderHistory(history, currentVersion);

return renderedHistory.length === 0 ? null : (
<div className="flex flex-col h-screen">
<h1 className="font-bold mb-2">Versions</h1>
<ul className="space-y-0 flex flex-col-reverse">
{history.map((item, index) => (
{renderedHistory.map((item, index) => (
<li key={index}>
<HoverCard>
<HoverCardTrigger
className={classNames(
"flex items-center justify-between space-x-2 p-2",
"border-b cursor-pointer",
{
" hover:bg-black hover:text-white":
index !== currentVersion,
"bg-slate-500 text-white": index === currentVersion,
" hover:bg-black hover:text-white": !item.isActive,
"bg-slate-500 text-white": item.isActive,
}
)}
onClick={() =>
Expand All @@ -63,32 +50,16 @@ export default function HistoryDisplay({
>
{" "}
<div className="flex gap-x-1 truncate">
<h2 className="text-sm truncate">
{item.type === "ai_edit"
? item.inputs.prompt
: item.type === "ai_create"
? "Create"
: "Imported from code"}
</h2>
{/* <h2 className="text-sm">{displayHistoryItemType(item.type)}</h2> */}
{item.parentIndex !== null &&
item.parentIndex !== index - 1 ? (
<h2 className="text-sm">
(parent: v{(item.parentIndex || 0) + 1})
</h2>
) : null}
<h2 className="text-sm truncate">{item.summary}</h2>
{item.parentVersion !== null && (
<h2 className="text-sm">(parent: {item.parentVersion})</h2>
)}
</div>
<h2 className="text-sm">v{index + 1}</h2>
</HoverCardTrigger>
<HoverCardContent>
<div>
{item.type === "ai_edit"
? item.inputs.prompt
: item.type === "ai_create"
? "Create"
: "Imported from code"}
</div>
<Badge>{displayHistoryItemType(item.type)}</Badge>
<div>{item.summary}</div>
<Badge>{item.type}</Badge>
</HoverCardContent>
</HoverCard>
</li>
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/components/history/history_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,10 @@ export type CodeCreateInputs = {
};

export type History = HistoryItem[];

export type RenderedHistoryItem = {
type: string;
summary: string;
parentVersion: string | null;
isActive: boolean;
};
109 changes: 108 additions & 1 deletion frontend/src/components/history/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect, test } from "vitest";
import { extractHistoryTree } from "./utils";
import { extractHistoryTree, renderHistory } from "./utils";
import type { History } from "./history_types";

const basicLinearHistory: History = [
Expand Down Expand Up @@ -29,6 +29,18 @@ const basicLinearHistory: History = [
},
];

const basicLinearHistoryWithCode: History = [
{
type: "code_create",
parentIndex: null,
code: "<html>1. create</html>",
inputs: {
code: "<html>1. create</html>",
},
},
...basicLinearHistory.slice(1),
];

const basicBranchingHistory: History = [
...basicLinearHistory,
{
Expand Down Expand Up @@ -121,3 +133,98 @@ test("should correctly extract the history tree", () => {
// Bad tree
expect(() => extractHistoryTree(basicBadHistory, 1)).toThrow();
});

test("should correctly render the history tree", () => {
expect(renderHistory(basicLinearHistory, 2)).toEqual([
{
isActive: false,
parentVersion: null,
summary: "Create",
type: "Create",
},
{
isActive: false,
parentVersion: null,
summary: "use better icons",
type: "Edit",
},
{
isActive: true,
parentVersion: null,
summary: "make text red",
type: "Edit",
},
]);

// Current version is the first version
expect(renderHistory(basicLinearHistory, 0)).toEqual([
{
isActive: true,
parentVersion: null,
summary: "Create",
type: "Create",
},
{
isActive: false,
parentVersion: null,
summary: "use better icons",
type: "Edit",
},
{
isActive: false,
parentVersion: null,
summary: "make text red",
type: "Edit",
},
]);

// Render a history with code
expect(renderHistory(basicLinearHistoryWithCode, 0)).toEqual([
{
isActive: true,
parentVersion: null,
summary: "Imported from code",
type: "Imported from code",
},
{
isActive: false,
parentVersion: null,
summary: "use better icons",
type: "Edit",
},
{
isActive: false,
parentVersion: null,
summary: "make text red",
type: "Edit",
},
]);

// Render a non-linear history
expect(renderHistory(basicBranchingHistory, 3)).toEqual([
{
isActive: false,
parentVersion: null,
summary: "Create",
type: "Create",
},
{
isActive: false,
parentVersion: null,
summary: "use better icons",
type: "Edit",
},
{
isActive: false,
parentVersion: null,
summary: "make text red",
type: "Edit",
},
{
isActive: true,
parentVersion: "v2",
summary: "make text green",
type: "Edit",
},
]);
});
66 changes: 65 additions & 1 deletion frontend/src/components/history/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { History, HistoryItem } from "./history_types";
import {
History,
HistoryItem,
HistoryItemType,
RenderedHistoryItem,
} from "./history_types";

export function extractHistoryTree(
history: History,
Expand Down Expand Up @@ -30,3 +35,62 @@ export function extractHistoryTree(

return flatHistory;
}

function displayHistoryItemType(itemType: HistoryItemType) {
switch (itemType) {
case "ai_create":
return "Create";
case "ai_edit":
return "Edit";
case "code_create":
return "Imported from code";
default: {
const exhaustiveCheck: never = itemType;
throw new Error(`Unhandled case: ${exhaustiveCheck}`);
}
}
}

function summarizeHistoryItem(item: HistoryItem) {
const itemType = item.type;
switch (itemType) {
case "ai_create":
return "Create";
case "ai_edit":
return item.inputs.prompt;
case "code_create":
return "Imported from code";
default: {
const exhaustiveCheck: never = itemType;
throw new Error(`Unhandled case: ${exhaustiveCheck}`);
}
}
}

export const renderHistory = (
history: History,
currentVersion: number | null
) => {
const renderedHistory: RenderedHistoryItem[] = [];

for (let i = 0; i < history.length; i++) {
const item = history[i];
// Only show the parent version if it's not the previous version
// (i.e. it's the branching point) and if it's not the first version
const parentVersion =
item.parentIndex !== null && item.parentIndex !== i - 1
? `v${(item.parentIndex || 0) + 1}`
: null;
const type = displayHistoryItemType(item.type);
const isActive = i === currentVersion;
const summary = summarizeHistoryItem(item);
renderedHistory.push({
isActive,
summary: summary,
parentVersion,
type,
});
}

return renderedHistory;
};

0 comments on commit b883201

Please sign in to comment.