Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
jessepinho committed Aug 1, 2024
1 parent c5df9f5 commit b9f13a6
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -1 +1,20 @@
export const AssetsPage = () => <div>Assets page</div>;
import { Table } from '@repo/ui/Table';

export const AssetsPage = () => (
<div className='flex flex-col gap-1'>
<Table title={<div>Account #1</div>}>
<Table.Thead>
<Table.Tr>
<Table.Th>Asset</Table.Th>
<Table.Th>Estimate</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
<Table.Tr>
<Table.Td>test</Table.Td>
<Table.Td>test</Table.Td>
</Table.Tr>
</Table.Tbody>
</Table>
</div>
);
63 changes: 63 additions & 0 deletions packages/ui/src/AddressViewComponent/address-view.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import type { Meta, StoryObj } from '@storybook/react';

import { AddressViewComponent } from '.';
import {
Address,
AddressIndex,
AddressView,
AddressView_Decoded,
} from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/keys/v1/keys_pb.js';
import { addressFromBech32m } from '@penumbra-zone/bech32m/penumbra';

const meta: Meta<typeof AddressViewComponent> = {
component: AddressViewComponent,
title: 'AddressViewComponent',
tags: ['autodocs'],
};
export default meta;

type Story = StoryObj<typeof AddressViewComponent>;

const EXAMPLE_VIEW = new AddressView({
addressView: {
case: 'decoded',

value: new AddressView_Decoded({
address: new Address({ inner: new Uint8Array(80) }),
index: new AddressIndex({
account: 0,
randomizer: new Uint8Array([0, 0, 0]),
}),
}),
},
});

const EXAMPLE_VIEW_OPAQUE = new AddressView({
addressView: {
case: 'opaque',
value: {
address: addressFromBech32m(
'penumbra1e8k5cyds484dxvapeamwveh5khqv4jsvyvaf5wwxaaccgfghm229qw03pcar3ryy8smptevstycch0qk3uu0rgkvtjpxy3cu3rjd0agawqtlz6erev28a6sg69u7cxy0t02nd4',
),
},
},
});

export const Decoded: Story = {
args: {
view: EXAMPLE_VIEW,
},
};

export const Copiable: Story = {
args: {
view: EXAMPLE_VIEW,
copyable: true,
},
};

export const Opaque: Story = {
args: {
view: EXAMPLE_VIEW_OPAQUE,
},
};
77 changes: 77 additions & 0 deletions packages/ui/src/AddressViewComponent/address-view.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import {
Address,
AddressIndex,
AddressView,
AddressView_Decoded,
} from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/keys/v1/keys_pb.js';
import { AddressViewComponent } from '.';
import { describe, expect, test } from 'vitest';
import { render } from '@testing-library/react';

const addressViewWithOneTimeAddress = new AddressView({
addressView: {
case: 'decoded',

value: new AddressView_Decoded({
address: new Address({ inner: new Uint8Array(80) }),
index: new AddressIndex({
account: 0,
// A one-time address is defined by a randomizer with at least one
// non-zero byte.
randomizer: new Uint8Array([1, 2, 3]),
}),
}),
},
});

const addressViewWithNormalAddress = new AddressView({
addressView: {
case: 'decoded',

value: new AddressView_Decoded({
address: new Address({ inner: new Uint8Array(80) }),
index: new AddressIndex({
account: 0,
randomizer: new Uint8Array([0, 0, 0]),
}),
}),
},
});

describe('<AddressViewComponent />', () => {
describe('when `copyable` is `true`', () => {
test('does not show the copy icon when the address is a one-time address', () => {
const { queryByTestId } = render(
<AddressViewComponent view={addressViewWithOneTimeAddress} copyable />,
);

expect(queryByTestId('CopyToClipboardIconButton__icon')).toBeNull();
});

test('shows the copy icon when the address is not a one-time address', () => {
const { queryByTestId } = render(
<AddressViewComponent view={addressViewWithNormalAddress} copyable />,
);

expect(queryByTestId('CopyToClipboardIconButton__icon')).not.toBeNull();
});
});

describe('when `copyable` is `false`', () => {
test('does not show the copy icon when the address is a one-time address', () => {
const { queryByTestId } = render(
<AddressViewComponent view={addressViewWithOneTimeAddress} copyable={false} />,
);

expect(queryByTestId('CopyToClipboardIconButton__icon')).toBeNull();
});

test('does not show the copy icon when the address is not a one-time address', () => {
const { queryByTestId } = render(
<AddressViewComponent view={addressViewWithNormalAddress} copyable={false} />,
);

expect(queryByTestId('CopyToClipboardIconButton__icon')).toBeNull();
});
});
});
51 changes: 51 additions & 0 deletions packages/ui/src/AddressViewComponent/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { AddressIcon } from '../address/address-icon';
import { AddressView } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/keys/v1/keys_pb.js';
import { CopyToClipboardIconButton } from '../copy-to-clipboard/copy-to-clipboard-icon-button';
import { AddressComponent } from '../address/address-component';
import { bech32mAddress } from '@penumbra-zone/bech32m/penumbra';

interface AddressViewProps {
view: AddressView | undefined;
copyable?: boolean;
}

// Renders an address or an address view.
// If the view is given and is "visible", the account information will be displayed instead.
export const AddressViewComponent = ({ view, copyable = true }: AddressViewProps) => {
if (!view?.addressView.value?.address) {
return <></>;
}

const encodedAddress = bech32mAddress(view.addressView.value.address);

const accountIndex =
view.addressView.case === 'decoded' ? view.addressView.value.index?.account : undefined;
const isOneTimeAddress =
view.addressView.case === 'decoded'
? !view.addressView.value.index?.randomizer.every(v => v === 0) // Randomized (and thus, a one-time address) if the randomizer is not all zeros.
: undefined;

const addressIndexLabel = isOneTimeAddress ? 'IBC Deposit Address for Account #' : 'Account #';

copyable = isOneTimeAddress ? false : copyable;

return (
<div className='flex items-center gap-2 overflow-hidden'>
{accountIndex !== undefined ? (
<>
<div className='shrink-0'>
<AddressIcon address={view.addressView.value.address} size={14} />
</div>
<span className='break-keep font-bold'>
{addressIndexLabel}
{accountIndex}
</span>
</>
) : (
<AddressComponent address={view.addressView.value.address} />
)}

{copyable && <CopyToClipboardIconButton text={encodedAddress} />}
</div>
);
};

0 comments on commit b9f13a6

Please sign in to comment.