-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c5df9f5
commit b9f13a6
Showing
4 changed files
with
211 additions
and
1 deletion.
There are no files selected for viewing
21 changes: 20 additions & 1 deletion
21
apps/minifront/src/components/v2/dashboard-layout/assets-page/index.tsx
This file contains 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 |
---|---|---|
@@ -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
63
packages/ui/src/AddressViewComponent/address-view.stories.tsx
This file contains 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,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
77
packages/ui/src/AddressViewComponent/address-view.test.tsx
This file contains 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,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(); | ||
}); | ||
}); | ||
}); |
This file contains 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,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> | ||
); | ||
}; |