Skip to content
This repository has been archived by the owner on Jan 24, 2025. It is now read-only.

Commit

Permalink
Add streams to data browser (#20)
Browse files Browse the repository at this point in the history
* Add stream data type to databrowser

* Organize useFetchSingleDataByKey to make it extendable

* Update colors of type tags

* Replace lucide react icons with radix icon svgs

* Update changeset

* Make style changes to buttons

* Prevent layout shifts

* Remove excess paddings

* Fix failling tests

* Reset datadisplay component when key changes && git push
  • Loading branch information
ogzhanolguncu authored Oct 12, 2023
1 parent 1be17a0 commit 119e7be
Show file tree
Hide file tree
Showing 28 changed files with 960 additions and 816 deletions.
5 changes: 5 additions & 0 deletions .changeset/slimy-jeans-serve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@upstash/react-databrowser": minor
---

Add ability to view streams in databrowser
2 changes: 1 addition & 1 deletion examples/nextjs13/app/databrowser/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default function DatabrowserDemo() {
style={{
height: "100%",
width: "100%",
maxHeight: "45rem",
maxHeight: "50rem",
maxWidth: "64rem",
borderRadius: "0.5rem",
overflow: "hidden",
Expand Down
4 changes: 2 additions & 2 deletions examples/nextjs13/e2e/databrowser-hash.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ test("should add data from cli then try to navigate on databrowser", async ({ pa

// Navigate through pages to find the last item
for (let i = 0; i < 4; i++) {
await page.getByRole("button", { name: "Next" }).click();
await page.getByTestId("datatable-next").click();
}
await expect(page.getByText("item50", { exact: true })).toBeVisible();

// Navigate back to find the first item again
for (let i = 0; i < 4; i++) {
await page.getByRole("button", { name: "Previous" }).click();
await page.getByTestId("datatable-prev").click();
}
await expect(page.getByText("item1", { exact: true })).toBeVisible();
});
16 changes: 8 additions & 8 deletions examples/nextjs13/e2e/databrowser-list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ test("should add data from cli then try to paginate on databrowser", async ({ pa
await page.getByPlaceholder("Search").fill("really");
await page.getByRole("button", { name: "really_long_list li" }).click();
await expect(page.getByText('"item1"', { exact: true })).toBeVisible();
await page.getByRole("button", { name: "Next" }).click();
await page.getByRole("button", { name: "Next" }).click();
await page.getByRole("button", { name: "Next" }).click();
await page.getByRole("button", { name: "Next" }).click();
await page.getByTestId("datatable-next").click();
await page.getByTestId("datatable-next").click();
await page.getByTestId("datatable-next").click();
await page.getByTestId("datatable-next").click();
await expect(page.getByText('"item50"', { exact: true })).toBeVisible();
await page.getByRole("button", { name: "Previous" }).click();
await page.getByRole("button", { name: "Previous" }).click();
await page.getByRole("button", { name: "Previous" }).click();
await page.getByRole("button", { name: "Previous" }).click();
await page.getByTestId("datatable-prev").click();
await page.getByTestId("datatable-prev").click();
await page.getByTestId("datatable-prev").click();
await page.getByTestId("datatable-prev").click();
await expect(page.getByText('"item1"', { exact: true })).toBeVisible();
});
4 changes: 2 additions & 2 deletions examples/nextjs13/e2e/databrowser-set.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ test("should add data from cli then try to navigate on databrowser", async ({ pa

// Navigate through pages to find the last item
for (let i = 0; i < 4; i++) {
await page.getByRole("button", { name: "Next" }).click();
await page.getByTestId("datatable-next").click();
}
await expect(page.getByText('"item50"', { exact: true })).toBeVisible();

// Navigate back to find the first item again
for (let i = 0; i < 4; i++) {
await page.getByRole("button", { name: "Previous" }).click();
await page.getByTestId("datatable-prev").click();
}
await expect(page.getByText('"item1"', { exact: true })).toBeVisible();
});
4 changes: 2 additions & 2 deletions examples/nextjs13/e2e/databrowser-zset.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ test("should add data from cli then try to navigate on databrowser", async ({ pa

// Navigate through pages to find the last item
for (let i = 0; i < 4; i++) {
await page.getByRole("button", { name: "Next" }).click();
await page.getByTestId("datatable-next").click();
}
await expect(page.getByText('"item50"', { exact: true })).toBeVisible();

// Navigate back to find the first item again
for (let i = 0; i < 4; i++) {
await page.getByRole("button", { name: "Previous" }).click();
await page.getByTestId("datatable-prev").click();
}
await expect(page.getByText('"item1"', { exact: true })).toBeVisible();
});
3 changes: 1 addition & 2 deletions packages/react-databrowser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
"autoprefixer": "^10.4.14",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"lucide-react": "^0.279.0",
"postcss": "^8.4.31",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand All @@ -55,7 +54,7 @@
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-toast": "^1.1.5",
"@upstash/redis": "^1.22.1",
"@upstash/redis": "^1.23.3",
"react-query": "^3.39.3"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Spinner } from "@/components/ui/spinner";
import { Textarea } from "@/components/ui/textarea";
import { useToast } from "@/components/ui/use-toast";
import { RedisDataTypeUnion } from "@/types";
import { PlusCircledIcon } from "@radix-ui/react-icons";
import { Label } from "@radix-ui/react-label";
import { Loader2 } from "lucide-react";
import { FormEvent, useState } from "react";

const expUnit = ["Second(s)", "Minute(s)", "Hour(s)", "Day(s)", "Week(s)", "Month(s)", "Year(s)"] as const;
Expand All @@ -49,8 +49,8 @@ export function AddDataDialog({ onNewDataAdd }: Props) {
const key = formData.get("key") as string;
const value = formData.get("value") as string;
const exp = Number(formData.get("exp"));
const expUnit = formData.get("exp-unit") as ExpUnitUnion;
const ttl = convertToSeconds(expUnit, exp);
const expUnit = formData.get("exp-unit") as ExpUnitUnion | undefined;
const ttl = expUnit ? convertToSeconds(expUnit, exp) : null;
const ok = await addData.mutateAsync([key, value, ttl]);
if (!(key && value)) {
throw new Error("Missing key or value data");
Expand Down Expand Up @@ -156,14 +156,9 @@ export function AddDataDialog({ onNewDataAdd }: Props) {
</div>
<DialogFooter>
<Button type="submit" disabled={addData.isLoading}>
{addData.isLoading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Please wait
</>
) : (
"Save changes"
)}
<Spinner isLoading={addData.isLoading} isLoadingText="Please wait">
Save changes
</Spinner>
</Button>
</DialogFooter>
</form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const DataDisplayHeader = ({ selectedDataKey, onDataKeyChange }: Props) =
</TTLPopover>
<div className="ml-auto">
<DeleteAlertDialog onDeleteConfirm={handleDeleteKey}>
<Button>
<Button variant="secondary" className="border-1 border">
<MinusCircledIcon className="mr-2 h-4 w-4" />
Delete
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useFetchSingleDataByKey } from "@/components/databrowser/hooks/useFetch
import { RedisTypeTag } from "@/components/databrowser/type-tag";
import { DataTable } from "./data-table";
import { DisplayScrollarea } from "./display-scrollarea";
import { MissingDataDisplay } from "./missing-data-display";

type Props = {
selectedDataKeyTypePair: [string, RedisDataTypeUnion];
Expand All @@ -25,17 +26,21 @@ export function DataDisplay({ selectedDataKeyTypePair }: Props) {
</div>
</div>
{isLoading || error ? (
<Skeleton className="my-4 flex h-[400px] rounded p-4 shadow-[rgba(17,_17,_26,_0.1)_0px_0px_16px] transition-all" />
<Skeleton className="my-4 flex h-[548px] rounded p-4 shadow-[rgba(17,_17,_26,_0.1)_0px_0px_16px] transition-all" />
) : (keyType === "string" && data?.type === "string") || (keyType === "json" && data?.type === "json") ? (
<DisplayScrollarea data={data.content} />
) : keyType === "zset" && data?.type === "zset" ? (
<DataTable data={data.content} navigation={navigation} tableHeaders={["Score", "Content"]} />
<DataTable data={data.content} navigation={navigation} tableHeaders={["Score", "Members"]} />
) : keyType === "hash" && data?.type === "hash" ? (
<DataTable data={data.content} navigation={navigation} tableHeaders={["Field", "Content"]} />
<DataTable data={data.content} navigation={navigation} tableHeaders={["Field", "Fields"]} />
) : keyType === "list" && data?.type === "list" ? (
<DataTable data={data.content} navigation={navigation} tableHeaders={["Index", "Content"]} />
<DataTable data={data.content} navigation={navigation} tableHeaders={["Index", "Members"]} />
) : keyType === "set" && data?.type === "set" ? (
<DataTable data={data.content} navigation={navigation} tableHeaders={[null, "Content"]} />
<DataTable data={data.content} navigation={navigation} tableHeaders={[null, "Members"]} />
) : keyType === "stream" && data?.type === "stream" ? (
<DataTable data={data.content} navigation={navigation} tableHeaders={["StreamID", "Fields"]} />
) : data?.type === "unknown" ? (
<MissingDataDisplay />
) : null}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Navigation, ContentValue } from "@/components/databrowser/hooks/useFetchSingleDataByKey";
import { Navigation } from "@/components/databrowser/hooks/useFetchSingleDataByKey";
import { Button } from "@/components/ui/button";
import { CopyToClipboardButton, handleCopyClick } from "@/components/databrowser/copy-to-clipboard-button";
import { ContentValue } from "../../hooks/useFetchSingleDataByKey/utils";
import { ArrowLeftIcon, ArrowRightIcon } from "@radix-ui/react-icons";

type Props = {
data: ContentValue[];
Expand All @@ -11,25 +13,29 @@ type Props = {
export const DataTable = ({ data, navigation, tableHeaders }: Props) => {
return (
<div>
<Table className="my-4 rounded-md border border-dashed p-4 tracking-wide">
<Table className="my-4 min-h-[500px] rounded-md border border-dashed p-4 tracking-wide">
<TableCaption>
<div className="flex items-center justify-end">
<div className="space-x-2">
<div>
<div className="flex items-center gap-2">
<Button
data-testid="datatable-prev"
variant="outline"
size="sm"
size="icon"
className="ml-auto h-8 w-8 disabled:bg-[#8080803d]"
onClick={() => navigation.handlePageChange("prev")}
disabled={navigation.prevNotAllowed}
>
Previous
<ArrowLeftIcon />
</Button>
<Button
data-testid="datatable-next"
variant="outline"
size="sm"
size="icon"
className="h-8 w-8 disabled:bg-[#8080803d]"
onClick={() => navigation.handlePageChange("next")}
disabled={navigation.nextNotAllowed}
>
Next
<ArrowRightIcon />
</Button>
</div>
</div>
Expand All @@ -45,7 +51,7 @@ export const DataTable = ({ data, navigation, tableHeaders }: Props) => {
<TableRow key={idx}>
{item.value !== null ? <TableCell className="text-[12px] font-medium">{item.value}</TableCell> : null}
{item.content !== null ? (
<TableCell className="flex w-full justify-between text-[12px] font-medium">
<TableCell className="flex w-full justify-between whitespace-break-spaces text-[12px] font-medium">
{item.content}
<CopyToClipboardButton
sizeVariant="icon-xs"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const DataDisplayContainer = ({ selectedDataKeyTypePair, onDataKeyChange
<div className="h-full px-4 py-6 lg:px-8">
<div className="h-full space-y-6">
<DataDisplayHeader selectedDataKey={selectedDataKeyTypePair[0]} onDataKeyChange={onDataKeyChange} />
<DataDisplay selectedDataKeyTypePair={selectedDataKeyTypePair} />
<DataDisplay selectedDataKeyTypePair={selectedDataKeyTypePair} key={selectedDataKeyTypePair[0]} />
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useToast } from "@/components/ui/use-toast";
import { usePersistTTL, useUpdateTTL } from "@/components/databrowser/hooks/useUpdateTTL";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Label } from "@/components/ui/label";
import { Loader2 } from "lucide-react";
import { Spinner } from "@/components/ui/spinner";

// We show None when expiration we recieve from server is -1
const PERSISTED_KEY = -1;
Expand Down Expand Up @@ -99,14 +99,9 @@ export function TTLPopover({ children, TTL, dataKey }: PropsWithChildren<{ TTL?:
</span>

<Button size="sm" onClick={handlePersistTTL}>
{persistTTL.isLoading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Please wait
</>
) : (
"Persist Key"
)}
<Spinner isLoading={persistTTL.isLoading} isLoadingText="Please wait">
Persist Key
</Spinner>
</Button>
</div>
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function Sidebar({ onDataKeyChange, selectedDataKey }: Props) {

return (
<div className="flex flex-col">
<div className="min-h-[540px] flex-1 space-y-4 overflow-y-auto py-4">
<div className="min-h-[670px] flex-1 space-y-4 overflow-y-auto py-4">
<div className="px-3 py-2">
<div className="mb-3 flex items-center space-x-1">
<div className="relative">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Skeleton } from "@/components/ui/skeleton";
import { DEFAULT_FETCH_COUNT } from "../../hooks/useFetchPaginatedKeys";

export const LoadingSkeleton = () => (
<div className="space-y-1">
{Array(10)
{Array(DEFAULT_FETCH_COUNT)
.fill(0)
.map((_, idx) => (
<Skeleton className="h-[40px] w-full rounded" key={idx} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useQuery } from "react-query";
import { useDebounce } from "./useDebounce";
import { useDatabrowser } from "@/store";

const DEFAULT_FETCH_COUNT = 10;
export const DEFAULT_FETCH_COUNT = 13;
const INITIAL_CURSOR_NUM = 0;
const SCAN_MATCH_ALL = "*";
const DEBOUNCE_TIME = 250;
Expand Down
Loading

1 comment on commit 119e7be

@vercel
Copy link

@vercel vercel bot commented on 119e7be Oct 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

react-ui – ./

upstash-react-cli.vercel.app
react-ui-git-main-upstash.vercel.app
react-ui-upstash.vercel.app

Please sign in to comment.