Skip to content

Commit

Permalink
feat: create Rollup filter & FilterPanel (#496)
Browse files Browse the repository at this point in the history
* refactor(web): create Option type for Dropdown options

* chore(web): update changeset

* refactor(web): use label in Dropdown selected option display

* refactor(web): update Dropdown component main filename

* refactor(web): remove nested /components folder in Dropdown

* feat(web): render value when no label in Dropdown

* feat(web): add prefix slot to Dropdown option

* feat(web): add filter panel with rollup filter in PaginatedTable

* chore(web): update changeset

* refactor(web): rename main PaginedTable filename

* refactor(web): rename main FilersPanel filename

* refactor(web): update RollupFilter component to fetch rollups from API

* feat(web): add filters to query params

* fix(web): pages number calculation in PaginatedTable

* fix(web): pages number calculation in PaginatedTable

* refactor(web): simplify dropdowns options

* feat(web): add clear button for filter

* Separate Prisma enums to their own file (#523)

* chore: add `@prisma/generator-helper` dep

* chore(db): set up prisma enum generator

* refactor: import prisma enums from generated file instead of prisma client

* chore(db): add comment to enum generator referencing source

* fix(web): rollback `StorageIcon` prop change

* chore(vercel): run prisma generation and migrations within `db` package

* fix(web): resolve rollup filtering issue

* chore: add changeset

* Abstract rollup addresses into its own package (#524)

* chore: add `@prisma/generator-helper` dep

* chore(db): set up prisma enum generator

* refactor: import prisma enums from generated file instead of prisma client

* chore(db): add comment to enum generator referencing source

* fix(web): rollback `StorageIcon` prop change

* chore(vercel): run prisma generation and migrations within `db` package

* fix(web): resolve rollup filtering issue

* chore: add changeset

* feat: create rollups package

* refactor: use `@blobscan/rollups` package

* feat(rollups): add support for retrieving a rollup by its address

* chore: add changeset

* feat(rollups): add support for retrieving all chain's available rollups

* chore: add changeset

* test(rollups): add tests

* test(rollups): add tests to verify correct behavior when retrieving non-existent values

* chore(rollups): set ininitial version

* feat(docs): list `rollups` package

* chore: fix `@blobscan/rollups` version

* refactor(web): display selected rollup icon on rollup dropdown

* refactor(web): refactor `Filters` component

* feat(web): add support for filtering by no rollup

* feat(web): add clear button to dropdown + make rollup filter clearable

* chore: add changeset

* refactor(web): extract query parameter logic into a custom hook

* fix(web): sync rollup filter dropdown with URL query parameter for consistent selection

* feat(rollups): return rollup's address and name when fetching chain's rollups

* chore: add changeset

* fix(web): use rollup's address when filtering elements by rollup

* chore: add changeset

* fix(web): ensure rollup address query parameter stays consistent with rollup filter dropdown value

---------

Co-authored-by: Luis Herasme <[email protected]>
Co-authored-by: elessar.eth <[email protected]>
  • Loading branch information
3 people authored Aug 31, 2024
1 parent 04ae214 commit 16870e4
Show file tree
Hide file tree
Showing 76 changed files with 800 additions and 374 deletions.
5 changes: 5 additions & 0 deletions .changeset/happy-books-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@blobscan/rollups": minor
---

Updated the chain rollups retrieval to return both the rollup name and address for each rollup.
5 changes: 5 additions & 0 deletions .changeset/nasty-paws-trade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@blobscan/web": minor
---

Enhanced dropdowns with clearable option support
5 changes: 5 additions & 0 deletions .changeset/odd-pumpkins-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@blobscan/rollups": minor
---

Added support for retrieving rollups by their addresses
5 changes: 5 additions & 0 deletions .changeset/quiet-experts-lie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@blobscan/rollups": minor
---

Added support for retrieving all chain's available rollups
5 changes: 5 additions & 0 deletions .changeset/shaggy-adults-guess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@blobscan/web": patch
---

Added support for clearing dropdown
5 changes: 5 additions & 0 deletions .changeset/sixty-parrots-kneel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@blobscan/db": minor
---

Exposed Prisma enums from a separated file
5 changes: 5 additions & 0 deletions .changeset/smooth-trains-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@blobscan/web": minor
---

Added rollup filter to blobs, blocks and transactions views
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ coverage
**/prisma/db.sqlite
**/prisma/db.sqlite-journal

# Prisma generated files
packages/db/prisma/enums

# next.js
.next/
out/
Expand Down
1 change: 1 addition & 0 deletions apps/docs/src/app/docs/codebase-overview/page.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Here you can find all the shared packages used by the apps:
| [`@blobscan/test`](https://github.com/Blobscan/blobscan/tree/next/packages/test) |  Shared test utilities and fixtures. |
|  [`@blobscan/zod`](https://github.com/Blobscan/blobscan/tree/next/packages/zod) |  Shared [Zod](https://zod.dev) schemas and utilities. |
|  [`@blobscan/eth-format`](https://github.com/Blobscan/blobscan/tree/next/packages/eth-format) |  Provides utility functions for handling Ethereum value conversions and formatting. |
|  [`@blobscan/rollups`](https://github.com/Blobscan/blobscan/tree/next/packages/rollups) |  A utility that provides a comprehensive list of all rollups and their associated addresses supported by Blobscan, along with functions to retrieve them easily. |

### Tooling

Expand Down
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@blobscan/env": "workspace:^0.0.1",
"@blobscan/eth-units": "workspace:^0.0.1",
"@blobscan/open-telemetry": "workspace:^0.0.8",
"@blobscan/rollups": "workspace:^0.0.1",
"@fontsource/inter": "^4.5.15",
"@fontsource/public-sans": "^4.5.12",
"@headlessui/react": "^2.1.0",
Expand Down
6 changes: 3 additions & 3 deletions apps/web/src/components/Cards/SurfaceCards/BlobCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ const BlobCard: FC<BlobCardProps> = ({
</div>
{transactions
?.filter((tx) => !!tx.rollup)
.map(({ rollup }) => (
<RollupIcon key={rollup} rollup={rollup as Rollup} />
))}
.map(({ rollup }) =>
rollup ? <RollupIcon key={rollup} rollup={rollup} /> : null
)}
</div>
) : (
<Skeleton width={isCompact ? undefined : 630} />
Expand Down
6 changes: 4 additions & 2 deletions apps/web/src/components/Dropdown/Option.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ListboxOption } from "@headlessui/react";
import type { Option as OptionProps } from ".";

export const Option: React.FC<OptionProps> = function (props) {
const { prefix, label, value } = props;
return (
<ListboxOption
className={({ focus }) =>
Expand All @@ -15,11 +16,12 @@ export const Option: React.FC<OptionProps> = function (props) {
value={props}
>
{({ selected }) => (
<div className="flex items-center">
<div className="flex items-center gap-3">
{prefix && prefix}
<span
className={`block truncate text-sm ${selected ? "font-bold" : ""}`}
>
{props.label ? props.label : props.value}
{label ? label : value}
</span>
</div>
)}
Expand Down
40 changes: 29 additions & 11 deletions apps/web/src/components/Dropdown/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,22 @@ import {
ListboxOptions,
Transition,
} from "@headlessui/react";
import { ChevronUpDownIcon } from "@heroicons/react/24/outline";
import { ChevronUpDownIcon, XMarkIcon } from "@heroicons/react/24/solid";

import { Option } from "./Option";

export interface Option {
value: string | number;
label?: ReactNode;
prefix?: ReactNode;
}

export interface DropdownProps {
options: Option[];
selected: Option;
selected: Option | null;
width?: string;
onChange(newOption: Option): void;
placeholder?: string;
clearable?: boolean;
onChange(newOption: Option | null): void;
}

const DEFAULT_WIDTH = "w-32";
Expand All @@ -28,24 +30,40 @@ export const Dropdown: React.FC<DropdownProps> = function ({
selected,
width,
onChange,
clearable = false,
placeholder = "Select an item",
}) {
return (
<Listbox value={selected} onChange={onChange}>
<div className="relative">
<ListboxButton
className={`relative h-9 ${
width ?? DEFAULT_WIDTH
} cursor-pointer rounded-lg border border-transparent bg-controlBackground-light pl-2 pr-8 text-left text-sm shadow-md hover:border hover:border-controlBackground-light focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white active:border-controlBorderHighlight-dark ui-open:border-controlActive-light dark:bg-controlBackground-dark dark:hover:border-controlBorderHighlight-dark dark:ui-open:border-controlActive-dark`}
} flex cursor-pointer items-center justify-between rounded-lg border border-transparent bg-controlBackground-light pl-2 pr-12 text-left text-sm shadow-md hover:border hover:border-controlBackground-light focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white active:border-controlBorderHighlight-dark ui-open:border-controlActive-light dark:bg-controlBackground-dark dark:hover:border-controlBorderHighlight-dark dark:ui-open:border-controlActive-dark`}
>
<span className="block truncate align-middle font-normal">
{selected.label ? selected.label : selected.value}
</span>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<div className="truncate align-middle">
{selected ? selected.label ?? selected.value : placeholder}
</div>
<div className="absolute inset-y-0 right-0 flex items-center gap-1 pr-2">
{clearable && selected ? (
<>
<XMarkIcon
className="h-4 w-4 text-contentTertiary-light opacity-60 hover:text-iconHighlight-light hover:opacity-100 dark:text-contentTertiary-dark dark:hover:text-iconHighlight-dark"
onClick={(e) => {
e.stopPropagation();
onChange(null);
}}
/>
</>
) : null}
<div className="text-contentTertiary-light opacity-30 dark:text-contentTertiary-dark">
|
</div>
<ChevronUpDownIcon
className="h-5 w-5 text-icon-light dark:text-icon-dark"
className="pointer-events-none h-5 w-5 text-icon-light dark:text-icon-dark"
aria-hidden="true"
/>
</span>
</div>
</ListboxButton>
<Transition
leave="transition ease-in duration-100"
Expand Down
49 changes: 49 additions & 0 deletions apps/web/src/components/Filters/RollupFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { FC } from "react";

import { getChainRollups } from "@blobscan/rollups";

import { Dropdown } from "~/components/Dropdown";
import type { DropdownProps, Option } from "~/components/Dropdown";
import { RollupIcon } from "~/components/RollupIcon";
import { env } from "~/env.mjs";
import type { Rollup } from "~/types";
import { capitalize, getChainIdByName } from "~/utils";

type RollupFilterProps = Pick<DropdownProps, "selected"> & {
onChange(newRollup: Option | null): void;
};

const chainId = getChainIdByName(env.NEXT_PUBLIC_NETWORK_NAME);
const rollups = chainId ? getChainRollups(chainId) : [];

export const ROLLUP_OPTIONS: Option[] = [
{
value: "null",
label: "None",
},
...rollups.map(([rollupAddress, rollupName]) => ({
value: rollupAddress,
label: (
<div className="flex items-center gap-2">
<RollupIcon rollup={rollupName.toLowerCase() as Rollup} />
{capitalize(rollupName)}
</div>
),
})),
];

export const RollupFilter: FC<RollupFilterProps> = function ({
onChange,
selected,
}) {
return (
<Dropdown
selected={selected}
options={ROLLUP_OPTIONS}
onChange={onChange}
placeholder="Rollup"
width="w-40"
clearable
/>
);
};
67 changes: 67 additions & 0 deletions apps/web/src/components/Filters/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { useEffect, useState } from "react";
import type { FC } from "react";
import { useRouter } from "next/router";
import type { UrlObject } from "url";

import { Button } from "~/components/Button";
import { useQueryParams } from "~/hooks/useQueryParams";
import type { Option } from "../Dropdown";
import { ROLLUP_OPTIONS, RollupFilter } from "./RollupFilter";

export const Filters: FC = function () {
const router = useRouter();
const queryParams = useQueryParams();
const [selectedRollup, setSelectedRollup] = useState<Option | null>(null);
const disableClear = !selectedRollup;

const handleFilter = () => {
const query: UrlObject["query"] = {};

if (selectedRollup) {
if (selectedRollup.value === "null") {
query.rollup = selectedRollup.value;
} else {
query.from = selectedRollup.value;
}
}

router.push({
pathname: router.pathname,
query,
});
};

const handleClear = () => {
setSelectedRollup(null);
};

useEffect(() => {
if (queryParams.rollup || queryParams.from) {
const rollupOption = ROLLUP_OPTIONS.find(
(opt) =>
opt.value === queryParams.rollup || opt.value === queryParams.from
);

if (rollupOption) {
setSelectedRollup(rollupOption);
}
}
}, [queryParams]);

return (
<div className="flex flex-col justify-between gap-2 rounded-lg bg-slate-50 p-2 dark:bg-primary-900 sm:flex-row">
<RollupFilter
selected={selectedRollup}
onChange={(newRollup) => {
setSelectedRollup(newRollup);
}}
/>
<div className="flex flex-row gap-2">
<Button variant="outline" onClick={handleClear} disabled={disableClear}>
Clear
</Button>
<Button onClick={handleFilter}>Filter</Button>
</div>
</div>
);
};
23 changes: 14 additions & 9 deletions apps/web/src/components/Layouts/PaginatedListLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ export const PaginatedListLayout: FC<PaginatedListLayoutProps> = function ({
const hasItems = !items || items.length;

const handlePageSizeSelection = useCallback<DropdownProps["onChange"]>(
({ value: newPageSize }) =>
(option) => {
if (!option) {
return;
}

const newPageSize = option.value as number;

void router.push({
pathname: router.pathname,
query: {
Expand All @@ -56,26 +62,25 @@ export const PaginatedListLayout: FC<PaginatedListLayoutProps> = function ({
* Update the selected page to a lower value if we require less pages to show the
* new amount of elements per page.
*/
p: Math.min(
Math.ceil(totalItems ?? 0 / (newPageSize as number)),
page
),
p: Math.min(Math.ceil(totalItems ?? 0 / newPageSize), page),
ps: newPageSize,
},
}),
});
},
[page, totalItems, router]
);

const handlePageSelection = useCallback<PaginationProps["onChange"]>(
(newPage) =>
(newPage) => {
void router.push({
pathname: router.pathname,
query: {
...router.query,
p: newPage,
ps: pageSize,
},
}),
});
},
[pageSize, router]
);

Expand Down Expand Up @@ -119,7 +124,7 @@ export const PaginatedListLayout: FC<PaginatedListLayoutProps> = function ({
Displayed items:
<Dropdown
options={PAGE_SIZES_OPTIONS}
selected={{ value: pageSize, label: pageSize.toString() }}
selected={{ value: pageSize }}
width="w-full"
onChange={handlePageSizeSelection}
/>
Expand Down
Loading

0 comments on commit 16870e4

Please sign in to comment.