From b9f13a63077106b4b1d85157c66a06b1d92c2953 Mon Sep 17 00:00:00 2001 From: Jesse Pinho Date: Wed, 31 Jul 2024 18:57:13 -0700 Subject: [PATCH] wip --- .../v2/dashboard-layout/assets-page/index.tsx | 21 ++++- .../address-view.stories.tsx | 63 +++++++++++++++ .../address-view.test.tsx | 77 +++++++++++++++++++ .../ui/src/AddressViewComponent/index.tsx | 51 ++++++++++++ 4 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 packages/ui/src/AddressViewComponent/address-view.stories.tsx create mode 100644 packages/ui/src/AddressViewComponent/address-view.test.tsx create mode 100644 packages/ui/src/AddressViewComponent/index.tsx diff --git a/apps/minifront/src/components/v2/dashboard-layout/assets-page/index.tsx b/apps/minifront/src/components/v2/dashboard-layout/assets-page/index.tsx index 5d714e1f87..dda8ac37ef 100644 --- a/apps/minifront/src/components/v2/dashboard-layout/assets-page/index.tsx +++ b/apps/minifront/src/components/v2/dashboard-layout/assets-page/index.tsx @@ -1 +1,20 @@ -export const AssetsPage = () =>
Assets page
; +import { Table } from '@repo/ui/Table'; + +export const AssetsPage = () => ( +
+ Account #1}> + + + Asset + Estimate + + + + + test + test + + +
+
+); diff --git a/packages/ui/src/AddressViewComponent/address-view.stories.tsx b/packages/ui/src/AddressViewComponent/address-view.stories.tsx new file mode 100644 index 0000000000..7a1e98b391 --- /dev/null +++ b/packages/ui/src/AddressViewComponent/address-view.stories.tsx @@ -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 = { + component: AddressViewComponent, + title: 'AddressViewComponent', + tags: ['autodocs'], +}; +export default meta; + +type Story = StoryObj; + +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, + }, +}; diff --git a/packages/ui/src/AddressViewComponent/address-view.test.tsx b/packages/ui/src/AddressViewComponent/address-view.test.tsx new file mode 100644 index 0000000000..0083484d5f --- /dev/null +++ b/packages/ui/src/AddressViewComponent/address-view.test.tsx @@ -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('', () => { + describe('when `copyable` is `true`', () => { + test('does not show the copy icon when the address is a one-time address', () => { + const { queryByTestId } = render( + , + ); + + expect(queryByTestId('CopyToClipboardIconButton__icon')).toBeNull(); + }); + + test('shows the copy icon when the address is not a one-time address', () => { + const { queryByTestId } = render( + , + ); + + 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( + , + ); + + expect(queryByTestId('CopyToClipboardIconButton__icon')).toBeNull(); + }); + + test('does not show the copy icon when the address is not a one-time address', () => { + const { queryByTestId } = render( + , + ); + + expect(queryByTestId('CopyToClipboardIconButton__icon')).toBeNull(); + }); + }); +}); diff --git a/packages/ui/src/AddressViewComponent/index.tsx b/packages/ui/src/AddressViewComponent/index.tsx new file mode 100644 index 0000000000..03cd8ae901 --- /dev/null +++ b/packages/ui/src/AddressViewComponent/index.tsx @@ -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 ( +
+ {accountIndex !== undefined ? ( + <> +
+ +
+ + {addressIndexLabel} + {accountIndex} + + + ) : ( + + )} + + {copyable && } +
+ ); +};