-
Notifications
You must be signed in to change notification settings - Fork 36
chore(price-feed): added update parameters as a column in each table for EVM #739
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 4 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
589d79f
added update parameters as a column in each table for evm
nidhi-singh02 c61a8ff
apply pre-commit/prettier formatting
nidhi-singh02 0a723fc
pretty pretty
nidhi-singh02 b862a21
show 7 items at once if more
nidhi-singh02 1916d68
helper mapValues and using max height for table
nidhi-singh02 8e696b9
typescript version
nidhi-singh02 ca0af08
update param define structure
nidhi-singh02 16ae437
put values in each row and remove fixed row
nidhi-singh02 5361b8a
Merge branch 'main' into sponsored-feeds-evm-update
nidhi-singh02 8602e88
pre commit
nidhi-singh02 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
import { useState } from "react"; | ||
import CopyIcon from "./icons/CopyIcon"; | ||
|
||
interface SponsoredFeed { | ||
name: string; | ||
priceFeedId: string; | ||
updateParameters: string; | ||
} | ||
|
||
interface SponsoredFeedsTableProps { | ||
feeds: SponsoredFeed[]; | ||
networkName: string; | ||
} | ||
|
||
export const SponsoredFeedsTable = ({ | ||
feeds, | ||
networkName, | ||
}: SponsoredFeedsTableProps) => { | ||
const [copiedId, setCopiedId] = useState<string | null>(null); | ||
|
||
const copyToClipboard = (text: string) => { | ||
navigator.clipboard.writeText(text).then(() => { | ||
setCopiedId(text); | ||
setTimeout(() => setCopiedId(null), 2000); | ||
}); | ||
}; | ||
|
||
// Calculate parameter statistics | ||
const paramCounts = feeds.reduce((acc, feed) => { | ||
acc[feed.updateParameters] = (acc[feed.updateParameters] || 0) + 1; | ||
return acc; | ||
}, {} as Record<string, number>); | ||
|
||
const defaultParams = Object.entries(paramCounts).sort( | ||
([, a], [, b]) => b - a | ||
)[0][0]; | ||
|
||
// Calculate table height based on number of items | ||
// Each row is approximately 40px (py-2 = 8px top + 8px bottom + content height) | ||
// Header is approximately 48px (py-2 = 8px top + 8px bottom + font height) | ||
// Show 7 rows by default, then scroll - but maintain consistent minimum height | ||
const maxVisibleRows = 7; | ||
const shouldScroll = feeds.length > maxVisibleRows; | ||
const rowHeight = 56; // Increased row height to account for actual content height | ||
const headerHeight = 48; // Header height in pixels | ||
const exactTableHeight = `${headerHeight + maxVisibleRows * rowHeight}px`; // Exact height for 7 rows | ||
const tableHeight = shouldScroll ? exactTableHeight : "auto"; // Use exact height for scrollable tables | ||
nidhi-singh02 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return ( | ||
<div className="my-6"> | ||
<p className="mb-3"> | ||
The price feeds listed in the table below are currently sponsored in{" "} | ||
<strong>{networkName}</strong>. | ||
</p> | ||
|
||
<div className="border border-gray-200 dark:border-gray-700 rounded-lg"> | ||
{/* Summary bar */} | ||
<div className="bg-blue-50 dark:bg-blue-900/20 px-3 py-2 border-b border-gray-200 dark:border-gray-600"> | ||
<div className="flex flex-wrap items-center gap-4 text-sm"> | ||
<div className="flex items-center gap-1.5"> | ||
<div className="w-1.5 h-1.5 bg-green-500 rounded-full flex-shrink-0"></div> | ||
<span className="font-medium">Default:</span> | ||
<span | ||
dangerouslySetInnerHTML={{ | ||
__html: defaultParams.replace("<br/>", " / "), | ||
}} | ||
/> | ||
<span className="text-gray-500"> | ||
({paramCounts[defaultParams]}) | ||
</span> | ||
</div> | ||
{Object.entries(paramCounts) | ||
.filter(([params]) => params !== defaultParams) | ||
.map(([params, count]) => ( | ||
<div key={params} className="flex items-center gap-1.5"> | ||
<div className="w-1.5 h-1.5 bg-orange-500 rounded-full flex-shrink-0"></div> | ||
<span className="font-medium">Exception:</span> | ||
<span | ||
dangerouslySetInnerHTML={{ | ||
__html: params.replace("<br/>", " / "), | ||
nidhi-singh02 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}} | ||
/> | ||
<span className="text-gray-500">({count})</span> | ||
</div> | ||
))} | ||
</div> | ||
</div> | ||
|
||
{/* Table */} | ||
<div className="overflow-x-auto"> | ||
<div | ||
className={`${shouldScroll ? "overflow-y-auto" : ""}`} | ||
style={{ height: tableHeight }} | ||
> | ||
<table className="w-full text-sm min-w-full"> | ||
<thead className="sticky top-0 bg-gray-50 dark:bg-gray-800 z-30"> | ||
<tr> | ||
<th className="text-left px-3 py-2 font-semibold text-gray-900 dark:text-gray-100 border-b border-gray-200 dark:border-gray-600 min-w-[100px]"> | ||
Name | ||
</th> | ||
<th className="text-left px-3 py-2 font-semibold text-gray-900 dark:text-gray-100 border-b border-gray-200 dark:border-gray-600 min-w-[400px]"> | ||
Price Feed Id | ||
</th> | ||
<th className="text-left px-3 py-2 font-semibold text-gray-900 dark:text-gray-100 border-b border-gray-200 dark:border-gray-600 min-w-[200px]"> | ||
Update Parameters | ||
</th> | ||
</tr> | ||
</thead> | ||
<tbody className="bg-white dark:bg-gray-900"> | ||
{feeds.map((feed, index) => { | ||
const isDefault = feed.updateParameters === defaultParams; | ||
const prevFeed = feeds[index - 1]; | ||
const isFirstInGroup = | ||
!prevFeed || | ||
prevFeed.updateParameters !== feed.updateParameters; | ||
|
||
return ( | ||
<tr | ||
key={feed.priceFeedId} | ||
className={`border-b border-gray-100 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800/30 ${ | ||
isFirstInGroup | ||
? "sticky top-12 bg-white dark:bg-gray-900 z-20 shadow-sm" | ||
: "" | ||
}`} | ||
> | ||
<td className="px-3 py-2 align-top"> | ||
<span className="font-medium text-gray-900 dark:text-gray-100"> | ||
{feed.name} | ||
</span> | ||
</td> | ||
<td className="px-3 py-2 align-top"> | ||
<div className="flex items-start gap-2"> | ||
<code className="text-xs font-mono text-gray-600 dark:text-gray-400 flex-1 break-all leading-relaxed"> | ||
{feed.priceFeedId} | ||
</code> | ||
<button | ||
onClick={() => copyToClipboard(feed.priceFeedId)} | ||
className="p-1 hover:bg-gray-200 dark:hover:bg-gray-600 rounded flex-shrink-0 mt-0.5" | ||
title="Copy Price Feed ID" | ||
> | ||
{copiedId === feed.priceFeedId ? ( | ||
<span className="text-green-500 text-xs font-bold"> | ||
✓ | ||
</span> | ||
) : ( | ||
<CopyIcon className="w-3 h-3 text-gray-400" /> | ||
)} | ||
</button> | ||
</div> | ||
</td> | ||
<td className="px-3 py-2 align-top"> | ||
{isFirstInGroup ? ( | ||
<div className="flex items-start gap-1.5"> | ||
<div | ||
className={`w-1.5 h-1.5 rounded-full mt-1 flex-shrink-0 ${ | ||
isDefault ? "bg-green-500" : "bg-orange-500" | ||
}`} | ||
></div> | ||
<span | ||
className={`text-xs leading-relaxed font-medium ${ | ||
isDefault | ||
? "text-gray-700 dark:text-gray-300" | ||
: "text-orange-600 dark:text-orange-400" | ||
}`} | ||
dangerouslySetInnerHTML={{ | ||
__html: feed.updateParameters, | ||
}} | ||
/> | ||
</div> | ||
) : ( | ||
<div className="flex items-start gap-1.5 text-gray-400 text-xs"> | ||
<div className="w-1.5 h-1.5 bg-green-500 rounded-full mt-1 flex-shrink-0"></div> | ||
<span>Same as above</span> | ||
nidhi-singh02 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
</div> | ||
)} | ||
</td> | ||
</tr> | ||
); | ||
})} | ||
</tbody> | ||
</table> | ||
</div> | ||
</div> | ||
|
||
{/* Show count indicator when scrolling is needed */} | ||
{shouldScroll && ( | ||
<div className="px-3 py-1 bg-gray-50 dark:bg-gray-800 border-t border-gray-200 dark:border-gray-600 text-xs text-gray-500 text-center"> | ||
Showing {maxVisibleRows} of {feeds.length} feeds • Scroll to see | ||
more | ||
</div> | ||
)} | ||
</div> | ||
</div> | ||
); | ||
}; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super minor nit but I tend to find this kind of thing is more easy to understand when implemented as:
I will also typically create a
mapValues
helper (which really should be in the ES standard IMO and probably will be eventually) which I use all the time and makes the code more concise:For some reason,
reduce
seems to be a hard operation for a lot of folks to easily grok so I've started to move away from using it except in very specific scenarios. Here is a decent writeup for an eslint rule that I use nowadays that has some links to some threads on the topic.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, I have created a utility file for object called "ObjectHelpers" in the
utils
folder so can be used across.I will see to add a linter rule as well, good idea and thanks for sharing the reference that really helps.
for ensuring
Object.groupBy
can be used, had to increase the typescript version as it was not present in older versions.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For what it's worth, I maintain canned eslint and typescript configs which we use across most node packages here, and I try to keep things in the main monorepo up to date, which applies to a pretty large bulk of our Node work. However, this docs repo is just an old repo and we've been planning to replace the docs app for a long time, so it's in a bit of a weird state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect! Thanks a lot for sharing this. Yeah, I will import these config in my local and gonna make sure if we have these same config applicable to this documentation repo.