Skip to content

Commit 5c5af0e

Browse files
jonyburCopilot
andauthored
feat(packages): add TableElement and adapt FinalityProviderItem to use it (#45)
* feat(packages): add TableElement and adapt FianlityProviderItem to use it * Update packages/babylon-core-ui/src/widgets/sections/TableElement/TableElement.tsx Co-authored-by: Copilot <[email protected]> * fix(packages): add export * fix(packages): fix import --------- Co-authored-by: Copilot <[email protected]>
1 parent 75a6d51 commit 5c5af0e

File tree

6 files changed

+168
-14
lines changed

6 files changed

+168
-14
lines changed

packages/babylon-core-ui/src/elements/FinalityProviderItem/FinalityProviderItem.stories.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,22 @@ export const LongNames: Story = {
9191
onRemove: (bsnId: string) => alert(`Remove clicked for ${bsnId}`),
9292
},
9393
};
94+
95+
export const WithoutRemove: Story = {
96+
args: {
97+
bsnId: "bsn321",
98+
bsnName: "Babylon Network",
99+
bsnLogoUrl: "/images/fps/lombard.jpeg",
100+
provider: mockProvider,
101+
},
102+
};
103+
104+
export const AddressOnly: Story = {
105+
args: {
106+
bsnId: "bsnAddr",
107+
bsnName: "",
108+
bsnLogoUrl: undefined,
109+
address: "dfe19b1234567890f732de",
110+
provider: mockProvider,
111+
},
112+
};

packages/babylon-core-ui/src/elements/FinalityProviderItem/FinalityProviderItem.tsx

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,16 @@ interface Provider {
1212
description?: ProviderDescription;
1313
}
1414

15-
interface FinalityProviderItemProps {
15+
export interface FinalityProviderItemProps {
1616
bsnId: string;
1717
bsnName: string;
1818
bsnLogoUrl?: string;
19+
address?: string;
1920
provider: Provider;
20-
onRemove: (id?: string) => void;
21+
onRemove?: (id?: string) => void;
2122
}
2223

23-
export function FinalityProviderItem({ bsnId, bsnName, bsnLogoUrl, provider, onRemove }: FinalityProviderItemProps) {
24+
export function FinalityProviderItem({ bsnId, bsnName, bsnLogoUrl, address, provider, onRemove }: FinalityProviderItemProps) {
2425
if (!provider) return null;
2526

2627
const renderBsnLogo = () => {
@@ -29,6 +30,27 @@ export function FinalityProviderItem({ bsnId, bsnName, bsnLogoUrl, provider, onR
2930
return <Avatar url={bsnLogoUrl} alt={bsnName} variant="rounded" size="tiny" className="mr-1" />;
3031
};
3132

33+
const shortenAddress = (value: string): string => {
34+
const visibleChars = 6;
35+
if (!value || value.length <= visibleChars * 2) return value;
36+
return `${value.slice(0, visibleChars)}...${value.slice(-visibleChars)}`;
37+
};
38+
39+
const renderChainOrAddress = () => {
40+
if (address) {
41+
return (
42+
<div className="text-xs text-accent-secondary">{shortenAddress(address)}</div>
43+
);
44+
}
45+
46+
return (
47+
<div className="flex items-center text-xs text-accent-secondary">
48+
{renderBsnLogo()}
49+
{bsnName}
50+
</div>
51+
);
52+
};
53+
3254
return (
3355
<div className="flex flex-row items-center justify-between">
3456
<div className="flex h-10 flex-row gap-2">
@@ -39,22 +61,19 @@ export function FinalityProviderItem({ bsnId, bsnName, bsnLogoUrl, provider, onR
3961
size="lg"
4062
/>
4163
<div className="flex flex-col justify-center text-accent-primary">
42-
<div className="flex items-center text-xs text-accent-secondary">
43-
{renderBsnLogo()}
44-
{bsnName}
45-
</div>
64+
{renderChainOrAddress()}
4665
<Text as="div" className="text-base font-medium text-accent-primary">
4766
{provider.description?.moniker}
4867
</Text>
4968
</div>
5069
</div>
51-
52-
<button
53-
onClick={() => onRemove(bsnId)}
54-
className="cursor-pointer rounded bg-accent-secondary/20 px-2 py-0.5 text-xs tracking-[0.4px] text-accent-primary"
55-
>
56-
Remove
57-
</button>
70+
{onRemove ?
71+
<button
72+
onClick={() => onRemove(bsnId)}
73+
className="cursor-pointer rounded bg-accent-secondary/20 px-2 py-0.5 text-xs tracking-[0.4px] text-accent-primary"
74+
>
75+
Remove
76+
</button> : null}
5877
</div>
5978
);
6079
}

packages/babylon-core-ui/src/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export * from "./widgets/form/RadioField";
2828
export * from "./widgets/form/SelectField";
2929
export * from "./widgets/form/HiddenField";
3030
export * from "./widgets/form/hooks";
31+
export * from "./widgets/sections/TableElement";
3132

3233
export * from "./widgets/sections/AmountSubsection";
3334
export * from "./widgets/sections/FinalityProviderSubsection";
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
3+
import { TableElement } from "./TableElement";
4+
5+
const meta: Meta<typeof TableElement> = {
6+
component: TableElement,
7+
tags: ["autodocs"],
8+
};
9+
10+
export default meta;
11+
12+
type Story = StoryObj<typeof meta>;
13+
14+
const sampleAttributes = {
15+
Balance: "100.123 BTC",
16+
"USD Value": "$4,567.89",
17+
Status: "Active",
18+
};
19+
20+
const mockProvider = {
21+
logo_url: undefined,
22+
rank: 1,
23+
description: {
24+
moniker: "Lombard Protocol",
25+
},
26+
};
27+
28+
const providerItem = {
29+
bsnId: "bsn123",
30+
bsnName: "Babylon",
31+
address: "dfe19b1234567890f732de",
32+
provider: mockProvider,
33+
// No onRemove to showcase address/noRemove mode
34+
};
35+
36+
export const Default: Story = {
37+
args: {
38+
providerItemProps: providerItem,
39+
attributes: sampleAttributes,
40+
onSelect: () => alert("Select clicked"),
41+
},
42+
};
43+
44+
export const Selected: Story = {
45+
args: {
46+
providerItemProps: providerItem,
47+
attributes: sampleAttributes,
48+
isSelected: true,
49+
onSelect: () => alert("Select clicked"),
50+
},
51+
};
52+
53+
export const NotSelectable: Story = {
54+
args: {
55+
providerItemProps: providerItem,
56+
attributes: sampleAttributes,
57+
isSelectable: false,
58+
onSelect: () => alert("Select clicked"),
59+
},
60+
};
61+
62+
export const WithoutHeader: Story = {
63+
args: {
64+
providerItemProps: providerItem,
65+
attributes: sampleAttributes,
66+
},
67+
};
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Button } from "../../../components/Button";
2+
import { FinalityProviderItem, FinalityProviderItemProps } from "../../../elements/FinalityProviderItem/FinalityProviderItem";
3+
4+
interface TableElementProps {
5+
providerItemProps: FinalityProviderItemProps;
6+
attributes: Record<string, React.ReactNode>;
7+
isSelected?: boolean;
8+
isSelectable?: boolean;
9+
onSelect?: () => void;
10+
}
11+
12+
export const TableElement = ({
13+
attributes,
14+
providerItemProps,
15+
isSelected = false,
16+
isSelectable = true,
17+
onSelect,
18+
}: TableElementProps) => {
19+
return (
20+
<div className="bg-secondary-highlight h-[316px] overflow-hidden p-4 flex flex-col rounded justify-between">
21+
<FinalityProviderItem {...providerItemProps} />
22+
23+
<div className="w-full h-px bg-secondary-strokeLight" />
24+
25+
{Object.entries(attributes).map(([label, value]) => (
26+
<div
27+
key={label}
28+
className="text-sm flex flex-row justify-between first:mt-0 mt-1"
29+
>
30+
<div className="text-accent-secondary">{label}</div>
31+
<div className="text-accent-primary font-medium">{value}</div>
32+
</div>
33+
))}
34+
35+
{onSelect && (
36+
<Button
37+
className="mt-4"
38+
onClick={onSelect}
39+
disabled={!isSelectable}
40+
variant={isSelected ? "contained" : "outlined"}
41+
>
42+
{isSelected ? "Selected" : "Select"}
43+
</Button>
44+
)}
45+
</div>
46+
);
47+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { TableElement } from "./TableElement"

0 commit comments

Comments
 (0)